Текстовые файлы

Рассмотрим работу с текстовым файлом в Си на примере. Создайте на диске С текстовый файл с именем TextFile.txt. Наберите в этом файле такие строки:

String_1 123 String_11, 456
String_2
String_3

Сохраните файл.

А это код программы на C, которая открывает наш файл и считывает из него строки:

/* *Author: @author Subbotin B.P..h> #include #define LEN 50 int main(void) { puts("Text file operations"); char cArray; FILE *pTextFile = fopen("C:\\TextFile.txt", "r"); if(pTextFile == NULL) { puts("Problems"); return EXIT_FAILURE; } while(fgets(cArray, LEN, pTextFile) != NULL) { printf("%s", cArray); } fclose(pTextFile); return EXIT_SUCCESS; }

Чтоб открыть текстовый файл в C используем функцию fopen:

FILE *pTextFile = fopen("C:\\TextFile.txt", "r");

первый аргумент функции fopen указывает на файл, а второй говорит, что файл открыт для чтения из него.

Строки считываем с помощью функции fgets:

fgets(cArray, LEN, pTextFile);

первый аргумент функции fgets указвает на массив символов, в котором будут сохранятся полученные строки, второй аргумент - это максимальное количество символов для считывания, третий - наш файл.

После завершения работы с файлом, его надо закрыть:

fclose(pTextFile);

Получаем:

Русские буквы в строках тоже проходят.

Кстати, эту программу я сделал в Eclipse. Как работать с C/C++ в Eclipse можно посмотреть .

Итак, мы открыли и считали данные из текстового файла.

Теперь научимся программно создавать текстовый файл и записывать в него данные.

/* Author: @author Subbotin B.P..h> #include int main(void) { FILE *pTextFile = fopen("C:\\TextFileW.txt", "w"); char *cString = "This is a string"; char cNewLine = "\n"; int nVal = 123; if(pTextFile == NULL) { puts("Problems"); return EXIT_FAILURE; } fprintf(pTextFile, "%s%c", cString, cNewLine); fprintf(pTextFile, "%d", nVal); return EXIT_SUCCESS; }

Создаем текстовый файл для записи в него данных:

FILE *pTextFile = fopen("C:\\TextFileW.txt", "w");

если файл уже имеется, то он будет открыт, и все данные из него будут удалены.

C-строка cString, и число nVal записываются программой в текстовый файл. cNewLine - это просто переход на новую строку.

Записываем данные в текстовый файл с помощью функции fprintf:

fprintf(pTextFile, "%s%c", cString, cNewLine);

первый аргумент здесь - наш файл, второй - форматная строка, третий и более - нужное для этого формата количество аргументов.

Для программиста открытый файл представляется как последовательность считываемых или записываемых данных. При открытии файла с ним связывается поток ввода-вывода . Выводимая информация записывается в поток, вводимая информация считывается из потока.

Когда поток открывается для ввода-вывода, он связывается со стандартной структурой типа FILE, которая определена в stdio.h. Структура FILE содержит необходимую информацию о файле.

Открытие файла осуществляется с помощью функции fopen(), которая возвращается указатель на структуру типа FILE, который можно использовать для последующих операций с файлом.

FILE *fopen (name, type);

name – имя открываемого файла (включая путь),
type - указатель на строку символов, определяющих способ доступа к файлу:

· "r" - открыть файл для чтения (файл должен существовать);

· "w" - открыть пустой файл для записи; если файл существует, то его содержимое теряется;

· "a" - открыть файл для записи в конец (для добавления); файл создается, если он не существует;

· "r+" - открыть файл для чтения и записи (файл должен существовать);

· "w+" - открыть пустой файл для чтения и записи; если файл существует, то его содержимое теряется;

· "a+" - открыть файл для чтения и дополнения, если файл не существует, то он создаётся.

Возвращаемое значение - указатель на открытый поток. Если обнаружена ошибка, то возвращается значение NULL.

Функция fclose() закрывает поток или потоки, связанные с открытыми при помощи функции fopen() файлами. Закрываемый поток определяется аргументом функции fclose().

Возвращаемое значение: значение 0, если поток успешно закрыт; константа EOF, если произошла ошибка.

#include
int main()

char name="my.txt";

if(fp = fopen(name, "r")!=NULL)

// открыть файлу далось?
... // требуемые действия над данными

else printf("Не удалось открыть файл");

Чтение символа из файла :

char fgetc(поток);

Аргументом функции является указатель на поток типа FILE. Функция возвращает код считанного символа. Если достигнут конец файла или возникла ошибка, возвращается константа EOF.
Запись символа в файл :

fputc(символ,поток);

Аргументами функции являются символ и указатель на поток типа FILE. Функция возвращает код считанного символа.

Функции fscanf() и fprintf() аналогичны функциям scanf() и printf(), но работают с файлами данных, и имеют первый аргумент - указатель на файл.

fscanf(поток, "Формат Ввода", аргументы);
fprintf(поток, "Формат Вывода", аргументы);

Функции fgets() и fputs() предназначены для ввода-вывода строк, они являются аналогами функций gets() и puts() для работы с файлами.

fgets(Указатель На Строку, Количество Символов, поток);

Символы читаются из потока до тех пор, пока не будет прочитан символ новой строки "\n", который включается в строку, или пока не наступит конец потока EOF или не будет прочитано максимальное символов. Результат помещается в указатель на строку и заканчивается нуль- символом "\0". Функция возвращает адрес строки.

fputs(Указатель На Строку, поток);

Копирует строку в поток с текущей позиции. Завершающий нуль- символ не копируется.
Пример Ввести число и сохранить его в файле s1.txt. Считать число из файла s1.txt, увеличить его на 3 и сохранить в файле s2.txt.

– сравнение для выявления равенства либо неравенства.

Практическое назначение перечисления – определение множества различающихся символических констант целого типа.

Пример использования переменных перечислимого типа:

mo=1, tu, we, th, fr, sa, su } days;

puts(“ Введите день недели (от 1 до 7) : ”); scanf(“%d”, &t_day);

w_day = su; start = mo;

end = w_day -t_day;

printf(“\n Понедельник - %d-й день недели, \ сейчас %d-й день. \n\

До конца недели %d дней (дня). ”, start, t_day, end);

Результат работы программы: Введите день недели (от 1 до 7) : 2

Понедельник - 1-й день недели, сейчас 2-й день. До конца недели 5 дней (дня).

18. Файлы в языке Си

Файл – это набор данных, размещенный на внешнем носителе и рассматриваемый в процессе обработки как единое целое. В файлах размещаются данные, предназначенные для длительного хранения.

Различают два вида файлов: текстовые и бинарные. Текстовые файлы представляют собой последовательность ASCII символов и могут быть просмотрены и отредактированы с помощью любого текстового редактора.

Бинарные (двоичные) файлы представляют собой последовательность данных, структура которых определяется программно.

В языке Си имеется большой набор функций для работы с файлами, большинство которых находятся в библиотеках stdio.h иio.h .

18.1. Открытие файла

Каждому файлу присваивается внутреннее логическое имя, используемое в дальнейшем при обращении к нему. Логическое имя (идентификатор файла) – это

указатель на файл, т.е. на область памяти, где содержится вся необходимая информация о файле. Формат объявления указателя на файл следующий:

FILE * указатель на файл;

FILE – идентификатор структурного типа, описанный в стандартной библиотеке

stdio.h и содержащий следующую информацию:

type struct {

– число оставшихся в буфере непрочитанных байт;

обычный размер буфера – 512 байт; как только level=0,

в буфер из файла читается следующий блок данных;

– флаг статуса файла – чтение, запись, дополнение;

– дескриптор файла, т.е. число, определяющее его но-

unsigned char hold;

– непереданный символ, т.е. ungetc-символ;

– размер внутреннего промежуточного буфера;

unsigned char buffer;

– значение указателя для доступа внутри буфера, т.е.

задает начало буфера, начало строки или текущее зна-

чение указателя внутри буфера в зависимости от режи-

ма буферизации;

unsigned char *curp;

– текущее значение указателя для доступа внутри бу-

фера, т.е. задает текущую позицию в буфере для обме-

на с программой;

unsigned istemp;

– флаг временного файла;

– флаг при работе с файлом;

} FILE;

Прежде чем начать работать с файлом, т.е. получить возможность чтения или записи информации в файл, его нужно открыть для доступа. Для этого обычно используется функция

FILE* fopen (char* имя_ файла, char* режим);

она берет внешнее представление – физическое имя файла на носителе (дискета, винчестер) и ставит ему в соответствие логическое имя.

Физическое имя, т.е. имя файла и путь к нему задается первым параметром

– строкой, например, “a:Mas_dat.dat” – файл с именем Mas_dat.dat, находящийся на дискете, “d:\\work\\Sved.txt” – файл с именем Sved.txt, находящийся на винчестере в каталоге work.

Внимание! Обратный слеш (\), как специальный символ, в строке записывается дважды.

При успешном открытии функция fopen возвращает указатель на файл (в дальнейшем – указатель файла). При ошибке возвращаетсяNULL . Данная ситуация обычно возникает, когда неверно указывается путь к открываемому файлу. Например, если в дисплейном классе нашего университета указать путь, запрещенный для записи (обычно разрешенным является d:\work\).

Второй параметр – строка, в которой задается режим доступа к файлу:

w – файл открывается для записи; если файла с заданным именем нет, то он будет создан; если такой файл существует, то перед открытием прежняя информация уничтожается;

r – файл открывается только для чтения; если такого файла нет, то возникает ошибка;

a – файл открывается для добавления в конец новой информации;

r+ – файл открывается для редактирования данных – возможны и запись, и чтение информации;

w+ – то же, что и для r+;

a+ – то же, что и для a, только запись можно выполнять в любое место файла; доступно и чтение файла;

t – файл открывается в текстовом режиме;b – файл открывается в двоичном режиме.

Текстовый режим отличается от двоичного тем, что при открытии файла как текстового пара символов «перевод строки», «возврат каретки» заменяется на один символ: «перевод строки» для всех функций записи данных в файл, а для всех функций вывода символ «перевод строки» теперь заменяется на два символа: «перевод строки», «возврат каретки».

По умолчанию файл открывается в текстовом режиме. Пример: FILE *f; – объявляется указатель на файл f;

f = fopen ("d:\\work\\Dat_sp.cpp", "w"); – открывается для записи файл с логическим именем f, имеющим физическое имя Dat_sp.cpp, находящийся на диске d, в каталоге work; или более кратко

FILE *f = fopen ("d:\\work\\Dat_sp.cpp", "w");

18.2. Закрытие файла

После работы с файлом доступ к нему необходимо закрыть. Это выполняет функция int fclose (указатель файла ). Например, из предыдущего примера файл закрывается так: fclose (f);

Для закрытия нескольких файлов введена функция, объявленная следующим образом: void fcloseall (void );

Если требуется изменить режим доступа к файлу, то для этого сначала необходимо закрыть данный файл, а затем вновь его открыть, но с другими правами доступа. Для этого используют стандартную функцию:

FILE* freopen (char*имя_файла , char *режим , FILE *указатель_файла );

Эта функция сначала закрывает файл, объявленный указателем_файла (как это делает функцияfopen ), а затем открывает файл сименем_файла и правами доступа «режим ».

В языке Си имеется возможность работы с временными файлами, которые нужны только в процессе работы программы. В этом случае используется функция

FILE* tmpfile (void);

которая создает на диске временный файл с правами доступа «w+b», после завершения работы программы или после закрытия временного файла он автоматически удаляется.

18.3. Запись – чтение информации

Все действия по чтению-записи данных в файл можно разделить на три группы: операции посимвольного ввода-вывода; операции построчного вводавывода; операции ввода-вывода по блокам.

Рассмотрим основные функции, применяемые в каждой из указанных трех групп операций.

Посимвольный ввод-вывод

В функциях посимвольного ввода-вывода происходит прием одного символа из файла или передача одного символа в файл:

Построчный ввод-вывод

В функциях построчного ввода-вывода происходит перенос из файла или в

Блоковый ввод-вывод

В функциях блокового ввода-вывода работа происходит с целыми блоками

информации:

int fread (void*p, intsize,

– считывает n блоков поsize байт каждый из фай-

int n, FILE *f)

ла f в область памяти с указателемp (необходимо

int fwrite (void*p, intsize,

заранее отвести память под считываемый блок);

– записывает n блоков по size байт каждый из об-

int n, FILE *f)

ласти памяти с указателем p в файл f.

Форматированный ввод-вывод производится функциями.

До этого при вводе-выводе данных мы работали со стандартными потоками - клавиатурой и монитором. Теперь рассмотрим, как в языке C реализовано получение данных из файлов и запись их туда. Перед тем как выполнять эти операции, надо открыть файл и получить доступ к нему.

В языке программирования C указатель на файл имеет тип FILE и его объявление выглядит так:
FILE *myfile;

С другой стороны, функция fopen() открывает файл по указанному в качестве первого аргумента адресу в режиме чтения ("r"), записи ("w") или добавления ("a") и возвращает в программу указатель на него. Поэтому процесс открытия файла и подключения его к программе выглядит примерно так:
myfile = fopen ("hello.txt", "r");

При чтении или записи данных в файл обращение к нему осуществляется посредством файлового указателя (в данном случае, myfile).

Если в силу тех или иных причин (нет файла по указанному адресу, запрещен доступ к нему) функция fopen() не может открыть файл, то она возвращает NULL. В реальных программах почти всегда обрабатывают ошибку открытия файла в ветке if , мы же далее опустим это.

Объявление функции fopen() содержится в заголовочном файле stdio.h, поэтому требуется его подключение. Также в stdio.h объявлен тип-структура FILE.

После того, как работа с файлом закончена, принято его закрывать, чтобы освободить буфер от данных и по другим причинам. Это особенно важно, если после работы с файлом программа продолжает выполняться. Разрыв связи между внешним файлом и указателем на него из программы выполняется с помощью функции fclose() . В качестве параметра ей передается указатель на файл:
fclose(myfile);

В программе может быть открыт не один файл. В таком случае каждый файл должен быть связан со своим файловым указателем. Однако если программа сначала работает с одним файлом, потом закрывает его, то указатель можно использовать для открытия второго файла.

Чтение из текстового файла и запись в него

fscanf()

Функция fscanf() аналогична по смыслу функции scanf() , но в отличии от нее осуществляет форматированный ввод из файла, а не стандартного потока ввода. Функция fscanf() принимает параметры: файловый указатель, строку формата, адреса областей памяти для записи данных:
fscanf (myfile, "%s%d", str, &a);

Возвращает количество удачно считанных данных или EOF. Пробелы, символы перехода на новую строку учитываются как разделители данных.

Допустим, у нас есть файл содержащий такое описание объектов:

Apples 10 23.4 bananas 5 25.0 bread 1 10.3

#include main () { FILE * file; struct food { char name[ 20 ] ; unsigned qty; float price; } ; struct food shop[ 10 ] ; char i= 0 ; file = fopen ("fscanf.txt" , "r" ) ; while (fscanf (file, "%s%u%f" , shop[ i] .name , & (shop[ i] .qty ) , & (shop[ i] .price ) ) != EOF) { printf ("%s %u %.2f\n " , shop[ i] .name , shop[ i] .qty , shop[ i] .price ) ; i++; } }

В данном случае объявляется структура и массив структур. Каждая строка из файла соответствует одному элементу массива; элемент массива представляет собой структуру, содержащую строковое и два числовых поля. За одну итерацию цикл считывает одну строку. Когда встречается конец файла fscanf() возвращает значение EOF и цикл завершается.

fgets()

Функция fgets() аналогична функции gets() и осуществляет построчный ввод из файла. Один вызов fgets() позволят прочитать одну строку. При этом можно прочитать не всю строку, а лишь ее часть от начала. Параметры fgets() выглядят таким образом:
fgets (массив_символов, количество_считываемых_символов, указатель_на_файл)

Например:
fgets (str, 50, myfile)

Такой вызов функции прочитает из файла, связанного с указателем myfile, одну строку текста полностью, если ее длина меньше 50 символов с учетом символа "\n", который функция также сохранит в массиве. Последним (50-ым) элементом массива str будет символ "\0", добавленный fgets() . Если строка окажется длиннее, то функция прочитает 49 символов и в конце запишет "\0". В таком случае "\n" в считанной строке содержаться не будет.

#include #define N 80 main () { FILE * file; char arr[ N] ; file = fopen ("fscanf.txt" , "r" ) ; while (fgets (arr, N, file) != NULL) printf ("%s" , arr) ; printf ("\n " ) ; fclose (file) ; }

В этой программе в отличие от предыдущей данные считываются строка за строкой в массив arr. Когда считывается следующая строка, предыдущая теряется. Функция fgets() возвращает NULL в случае, если не может прочитать следующую строку.

getc() или fgetc()

Функция getc() или fgetc() (работает и то и другое) позволяет получить из файла очередной один символ.

while ((arr[ i] = fgetc (file) ) != EOF) { if (arr[ i] == "\n " ) { arr[ i] = "\0 " ; printf ("%s\n " , arr) ; i = 0 ; } else i++; } arr[ i] = "\0 " ; printf ("%s\n " , arr) ;

Приведенный в качестве примера код выводит данные из файла на экран.

Запись в текстовый файл

Также как и ввод, вывод в файл может быть различным.

  • Форматированный вывод. Функция fprintf (файловый_указатель, строка_формата, переменные) .
  • Посточный вывод. Функция fputs (строка, файловый_указатель) .
  • Посимвольный вывод. Функция fputc() или putc(символ, файловый_указатель) .

Ниже приводятся примеры кода, в которых используются три способа вывода данных в файл.

Запись в каждую строку файла полей одной структуры:

file = fopen ("fprintf.txt" , "w" ) ; while (scanf ("%s%u%f" , shop[ i] .name , & (shop[ i] .qty ) , & (shop[ i] .price ) ) != EOF) { fprintf (file, "%s %u %.2f\n " , shop[ i] .name , shop[ i] .qty , shop[ i] .price ) ; i++; }

Построчный вывод в файл (fputs() , в отличие от puts() сама не помещает в конце строки "\n"):

while (gets (arr) != NULL) { fputs (arr, file) ; fputs ("\n " , file) ; }

Пример посимвольного вывода:

while ((i = getchar () ) != EOF) putc (i, file) ;

Чтение из двоичного файла и запись в него

С файлом можно работать не как с последовательностью символов, а как с последовательностью байтов. В принципе, с нетекстовыми файлами работать по-другому не возможно. Однако так можно читать и писать и в текстовые файлы. Преимущество такого способа доступа к файлу заключается в скорости чтения-записи: за одно обращение можно считать/записать существенный блок информации.

При открытии файла для двоичного доступа, вторым параметром функции fopen() является строка "rb" или "wb".

Тема о работе с двоичными файлами достаточно сложная, для ее изучения требуется отдельный урок. Здесь будут отмечены только особенности функций чтения-записи в файл, который рассматривается как поток байтов.

Функции fread() и fwrite() принимают в качестве параметров:

  1. адрес области памяти, куда данные записываются или откуда считываются,
  2. размер одного данного какого-либо типа,
  3. количество считываемых данных указанного размера,
  4. файловый указатель.

Эти функции возвращают количество успешно прочитанных или записанных данных. Т.е. можно "заказать" считывание 50 элементов данных, а получить только 10. Ошибки при этом не возникнет.

Пример использования функций fread() и fwrite() :

#include #include main () { FILE * file; char shelf1[ 50 ] , shelf2[ 100 ] ; int n, m; file = fopen ("shelf1.txt" , "rb" ) ; n= fread (shelf1, sizeof (char ) , 50 , file) ; fclose (file) ; file = fopen ("shelf2.txt" , "rb" ) ; m= fread (shelf2, sizeof (char ) , 50 , file) ; fclose (file) ; shelf1[ n] = "\0 " ; shelf2[ m] = "\n " ; shelf2[ m+ 1 ] = "\0 " ; file = fopen ("shop.txt" , "wb" ) ; fwrite (strcat (shelf2, shelf1) , sizeof (char ) , n+ m, file) ; fclose (file) ; }

Здесь осуществляется попытка чтения из первого файла 50-ти символов. В n сохраняется количество реально считанных символов. Значение n может быть равно 50 или меньше. Данные помещаются в строку. То же самое происходит со вторым файлом. Далее первая строка присоединяется ко второй, и данные сбрасываются в третий файл.

Решение задач

  1. Напишите программу, которая запрашивает у пользователя имя (адрес) текстового файла, далее открывает его и считает в нем количество символов и строк.
  2. Напишите программу, которая записывает в файл данные, полученные из другого файла и так или иначе измененные перед записью. Каждая строка данных, полученная из файла, должна помещаться в структуру.

Последнее обновление: 31.10.2015

Класс FileStream представляет возможности по считыванию из файла и записи в файл. Он позволяет работать как с текстовыми файлами, так и с бинарными.

Рассмотрим наиболее важные его свойства и методы:

    Свойство Length : возвращает длину потока в байтах

    Свойство Position : возвращает текущую позицию в потоке

    Метод Read : считывает данные из файла в массив байтов. Принимает три параметра: int Read(byte array, int offset, int count) и возвращает количество успешно считанных байтов. Здесь используются следующие параметры:

    • array - массив байтов, куда будут помещены считываемые из файла данные

      offset представляет смещение в байтах в массиве array, в который считанные байты будут помещены

      count - максимальное число байтов, предназначенных для чтения. Если в файле находится меньшее количество байтов, то все они будут считаны.

    Метод long Seek(long offset, SeekOrigin origin) : устанавливает позицию в потоке со смещением на количество байт, указанных в параметре offset.

    Метод Write : записывает в файл данные из массива байтов. Принимает три параметра: Write(byte array, int offset, int count)

    • array - массив байтов, откуда данные будут записываться в файла

      offset - смещение в байтах в массиве array, откуда начинается запись байтов в поток

      count - максимальное число байтов, предназначенных для записи

FileStream представляет доступ к файлам на уровне байтов, поэтому, например, если вам надо считать или записать одну или несколько строк в текстовый файл, то массив байтов надо преобразовать в строки, используя специальные методы. Поэтому для работы с текстовыми файлами применяются другие классы.

В то же время при работе с различными бинарными файлами, имеющими определенную структуру FileStream может быть очень даже полезен для извлечения определенных порций информации и ее обработки.

Посмотрим на примере считывания-записи в текстовый файл:

Console.WriteLine("Введите строку для записи в файл:"); string text = Console.ReadLine(); // запись в файл using (FileStream fstream = new FileStream(@"C:\SomeDir\noname\note.txt", FileMode.OpenOrCreate)) { // преобразуем строку в байты byte array = System.Text.Encoding.Default.GetBytes(text); // запись массива байтов в файл fstream.Write(array, 0, array.Length); Console.WriteLine("Текст записан в файл"); } // чтение из файла using (FileStream fstream = File.OpenRead(@"C:\SomeDir\noname\note.txt")) { // преобразуем строку в байты byte array = new byte; // считываем данные fstream.Read(array, 0, array.Length); // декодируем байты в строку string textFromFile = System.Text.Encoding.Default.GetString(array); Console.WriteLine("Текст из файла: {0}", textFromFile); } Console.ReadLine();

Разберем этот пример. И при чтении, и при записи используется оператор using . Не надо путать данный оператор с директивой using, которая подключает пространства имен в начале файла кода. Оператор using позволяет создавать объект в блоке кода, по завершению которого вызывается метод Dispose у этого объекта, и, таким образом, объект уничтожается. В данном случае в качестве такого объекта служит переменная fstream .

Объект fstream создается двумя разными способами: через конструктор и через один из статических методов класса File.

Здесь в конструктор передается два параметра: путь к файлу и перечисление FileMode . Данное перечисление указывает на режим доступа к файлу и может принимать следующие значения:

    Append : если файл существует, то текст добавляется в конец файл. Если файла нет, то он создается. Файл открывается только для записи.

    Create : создается новый файл. Если такой файл уже существует, то он перезаписывается

    CreateNew : создается новый файл. Если такой файл уже существует, то он приложение выбрасывает ошибку

    Open : открывает файл. Если файл не существует, выбрасывается исключение

    OpenOrCreate : если файл существует, он открывается, если нет - создается новый

    Truncate : если файл существует, то он перезаписывается. Файл открывается только для записи.

Статический метод OpenRead класса File открывает файл для чтения и возвращает объект FileStream.

Конструктор класса FileStream также имеет ряд перегруженных версий, позволяющий более точно настроить создаваемый объект. Все эти версии можно посмотреть на msdn.

И при записи, и при чтении применяется объект кодировки Encoding.Default из пространства имен System.Text . В данном случае мы используем два его метода: GetBytes для получения массива байтов из строки и GetString для получения строки из массива байтов.

В итоге введенная нами строка записывается в файл note.txt . По сути это бинарный файл (не текстовый), хотя если мы в него запишем только строку, то сможем посмотреть в удобочитаемом виде этот файл, открыв его в текстовом редакторе. Однако если мы в него запишем случайные байты, например:

Fstream.WriteByte(13); fstream.WriteByte(103);

То у нас могут возникнуть проблемы с его пониманием. Поэтому для работы непосредственно с текстовыми файлами предназначены отдельные классы - StreamReader и StreamWriter.

Произвольный доступ к файлам

Нередко бинарные файлы представляют определенную стрктуру. И, зная эту структуру, мы можем взять из файла нужную порцию информации или наоброт записать в определенном месте файла определенный набор байтов. Например, в wav-файлах непосредственно звуковые данные начинаются с 44 байта, а до 44 байта идут различные метаданные - количество каналов аудио, частота дискретизации и т.д.

С помощью метода Seek() мы можем управлять положением курсора потока, начиная с которого производится считывание или запись в файл. Этот метод принимает два параметра: offset (смещение) и позиция в файле. Позиция в файле описывается тремя значениями:

    SeekOrigin.Begin : начало файла

    SeekOrigin.End : конец файла

    SeekOrigin.Current : текущая позиция в файле

Курсор потока, с которого начинается чтение или запись, смещается вперед на значение offset относительно позиции, указанной в качестве второго параметра. Смещение может отрицательным, тогда курсор сдвигается назад, если положительное - то вперед.

Рассмотрим на примере:

Using System.IO; using System.Text; class Program { static void Main(string args) { string text = "hello world"; // запись в файл using (FileStream fstream = new FileStream(@"D:\note.dat", FileMode.OpenOrCreate)) { // преобразуем строку в байты byte input = Encoding.Default.GetBytes(text); // запись массива байтов в файл fstream.Write(input, 0, input.Length); Console.WriteLine("Текст записан в файл"); // перемещаем указатель в конец файла, до конца файла- пять байт fstream.Seek(-5, SeekOrigin.End); // минус 5 символов с конца потока // считываем четыре символов с текущей позиции byte output = new byte; fstream.Read(output, 0, output.Length); // декодируем байты в строку string textFromFile = Encoding.Default.GetString(output); Console.WriteLine("Текст из файла: {0}", textFromFile); // worl // заменим в файле слово world на слово house string replaceText = "house"; fstream.Seek(-5, SeekOrigin.End); // минус 5 символов с конца потока input = Encoding.Default.GetBytes(replaceText); fstream.Write(input, 0, input.Length); // считываем весь файл // возвращаем указатель в начало файла fstream.Seek(0, SeekOrigin.Begin); output = new byte; fstream.Read(output, 0, output.Length); // декодируем байты в строку textFromFile = Encoding.Default.GetString(output); Console.WriteLine("Текст из файла: {0}", textFromFile); // hello house } Console.Read(); } }

Консольный вывод:

Текст записан в файл Текст из файл: worl Текст из файла: hello house

Вызов fstream.Seek(-5, SeekOrigin.End) перемещает курсор потока в конец файлов назад на пять символов:

То есть после записи в новый файл строки "hello world" курсор будет стоять на позиции символа "w".

После этого считываем четыре байта начиная с символа "w". В данной кодировке 1 символ будет представлять 1 байт. Поэтому чтение 4 байтов будет эквивалентно чтению четырех сиволов: "worl".

Затем опять же перемещаемся в конец файла, не доходя до конца пять символов (то есть опять же с позиции символа "w"), и осуществляем запись строки "house". Таким образом, строка "house" заменяет строку "world".

Закрытие потока

В примерах выше дл закрытия потока применяется конструкция using . После того как все операторы и выражения в блоке using отработают, объект FileStream уничтожается. Однако мы можем выбрать и другой способ:

FileStream fstream = null; try { fstream = new FileStream(@"D:\note3.dat", FileMode.OpenOrCreate); // операции с потоком } catch(Exception ex) { } finally { if (fstream != null) fstream.Close(); }

Если мы не используем конструкцию using, то нам надо явным образом вызвать метод Close() : fstream.Close()



Эта статья также доступна на следующих языках: Тайский

  • Next

    Огромное Вам СПАСИБО за очень полезную информацию в статье. Очень понятно все изложено. Чувствуется, что проделана большая работа по анализу работы магазина eBay

    • Спасибо вам и другим постоянным читателям моего блога. Без вас у меня не было бы достаточной мотивации, чтобы посвящать много времени ведению этого сайта. У меня мозги так устроены: люблю копнуть вглубь, систематизировать разрозненные данные, пробовать то, что раньше до меня никто не делал, либо не смотрел под таким углом зрения. Жаль, что только нашим соотечественникам из-за кризиса в России отнюдь не до шоппинга на eBay. Покупают на Алиэкспрессе из Китая, так как там в разы дешевле товары (часто в ущерб качеству). Но онлайн-аукционы eBay, Amazon, ETSY легко дадут китайцам фору по ассортименту брендовых вещей, винтажных вещей, ручной работы и разных этнических товаров.

      • Next

        В ваших статьях ценно именно ваше личное отношение и анализ темы. Вы этот блог не бросайте, я сюда часто заглядываю. Нас таких много должно быть. Мне на эл. почту пришло недавно предложение о том, что научат торговать на Амазоне и eBay. И я вспомнила про ваши подробные статьи об этих торг. площ. Перечитала все заново и сделала вывод, что курсы- это лохотрон. Сама на eBay еще ничего не покупала. Я не из России , а из Казахстана (г. Алматы). Но нам тоже лишних трат пока не надо. Желаю вам удачи и берегите себя в азиатских краях.

  • Еще приятно, что попытки eBay по руссификации интерфейса для пользователей из России и стран СНГ, начали приносить плоды. Ведь подавляющая часть граждан стран бывшего СССР не сильна познаниями иностранных языков. Английский язык знают не более 5% населения. Среди молодежи — побольше. Поэтому хотя бы интерфейс на русском языке — это большая помощь для онлайн-шоппинга на этой торговой площадке. Ебей не пошел по пути китайского собрата Алиэкспресс, где совершается машинный (очень корявый и непонятный, местами вызывающий смех) перевод описания товаров. Надеюсь, что на более продвинутом этапе развития искусственного интеллекта станет реальностью качественный машинный перевод с любого языка на любой за считанные доли секунды. Пока имеем вот что (профиль одного из продавцов на ебей с русским интерфейсом, но англоязычным описанием):
    https://uploads.disquscdn.com/images/7a52c9a89108b922159a4fad35de0ab0bee0c8804b9731f56d8a1dc659655d60.png