Ftell дать позицию в файле

Содержание

Функция ftell

Функция ftell() возвращает текущее значение указателя позиции файла для заданного потока. В случае двоичных потоков это значение равно количеству байтов, которые отделяют указатель от начала файла. Для текстовых потоков возвращаемое значение может не иметь определенной интерпретации за исключением случая, когда оно является аргументом функции fseek() . Все дело в возможных преобразованиях символов, когда, например, комбинация кодов возврата каретки (ASCII 13) и конца строки (ASCII 10) заменяются разделителем строк, что влияет на размер файла.

При возникновении ошибки функция ftell() возвращает значение -1.

Пример

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

fgets — вывод ftell в функцию php

Результат как ниже:

Content-Length может не совпадать с моим — 518264, он будет динамически изменяться при выполнении кода, это не имеет значения для обсуждения.
Почему результат не следующий?

пожалуйста, объясните действие файлового указателя на функции fgets, ftell и fseek.

Решение

Согласно документации PHP для fgets () ,

Чтение заканчивается, когда прочитана длина — 1 байт, или новая строка (которая включена в возвращаемое значение), или EOF (что наступит раньше).

Вот длина это второй параметр, который вы использовали при вызове fgets() , который 1024. Итак, ваш призыв к fgets() закончит чтение, когда произойдет любое из следующего:

  1. Это прочитал +1023 байты из той же строки.
  2. Он достиг конца текущей строки.
  3. Это достигло конца файла.

Итак, в вашем случае, когда fgets() читает первую строку, она достигла конца первой строки после прочтения 16 байт, и это будет позиция указателя файла при вызове ftell() рядом с ним. ftell () возвращает текущую позицию указателя файла в файле.

Когда вы находитесь в следующей строке, вызывая fgets() снова (повторяется while цикл), ваша начальная позиция в файле сейчас 16 (в частности, не 1024), и вы можете прочитать до (16 + 1024) 1040 байты (не до 2048). Опять же, если ваша следующая строка имеет только 42 байты, это fgets() закончится чтение в 58 байты, которые будут позицией указателя файла при вызове ftell() снова.

И снова вы собираетесь начать дальше fgets() от 58 байты, для чтения до (58 + 1024 =) 1082 байт. Так будет и дальше.

Эффект fseek()
fseek() используется для перемещения файлового указателя на определенную позицию в файле, установленную $ офсет (2-й параматер). Согласно документации PHP для FSEEK () , значения 3-го параметра могут быть —

SEEK_SET — Установите позицию, равную байту смещения.
SEEK_CUR — Установить положение в текущее местоположение плюс смещение.
SEEK_END — Установить позицию в конец файла плюс смещение.

Итак, по fseek($handle,200000,SEEK_CUR); Вы устанавливаете указатель файла на 200000 + текущая позиция. Например, он перейдет на 200016, когда это было в 16.

Другие решения

Параметр длины в fgets указывает максимальную длину. PHP документация состояния:

Чтение заканчивается, когда прочитана длина — 1 байт, или новая строка (которая включена в возвращаемое значение), или EOF (что наступит раньше). Если длина не указана, он будет продолжать чтение из потока, пока не достигнет конца строки.

В вашем случае первая строка содержит , который объясняет результат 16, данный ftell ,

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

fgets ()

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

Возвращает строку длиной до 1 байта, прочитанную из файла, на который указывает дескриптор. Если в указателе файла больше нет данных для чтения, то FALSE возвращается

ftell ()

Встроенная функция: pos = ftell (fid)

Возвращает позицию указателя файла как количество символов от начала файла, указанного дескриптором файла fid ,

FSEEK ()

Встроенная функция: fseek (fid, offset)
Встроенная функция: fseek (fid, offset, origin)

Установите указатель файла на смещение местоположения в файле fid.

Указатель позиционируется смещенными символами от начала координат, которые могут быть одной из предопределенных переменных SEEK_CUR (current position), SEEK_SET (beginning), или же SEEK_END (end of file) или строки «cof», «bof» or «eof». Если источник не указан, предполагается SEEK_SET. смещение может быть положительным, отрицательным или нулевым, но не все комбинации начала и смещения могут быть реализованы.

fseek возвращает 0 в случае успеха и -1 в случае ошибки.

Используйте функцию PHP stream_get_meta_data() чтобы узнать, доступен ли открываемый вами поток:

Поток не доступен для поиска. Это означает, что функции fseek() , ftell() а также rewind() иметь неожиданное (и, вероятно, противоречивое) поведение.

Почему ftell пропускает некоторые позиции в файле?

У меня есть текстовый файл с именем myfile.txt, который гласит:

Всякий раз, когда \ п встречается, то ftell пропускает одно значение, например, оно пропущено значение 7. Почему это так? Пожалуйста, объясните мне подробно, я хочу знать.

Попробуйте изменить эти:

где b для двоичного режима.

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

ftell, _ftelli64 ftell, _ftelli64

Получает текущее положение указателя файла. Gets the current position of a file pointer.

Синтаксис Syntax

Параметры Parameters

вышестоящий stream
Структура целевого файла . Target FILE structure.

Возвращаемое значение Return Value

ftell и _ftelli64 возвращают текущее расположение файла. ftell and _ftelli64 return the current file position. Значение, возвращаемое ftell и _ftelli64 , может не отражать физический байт смещения для потоков, открытых в текстовом режиме, так как текстовый режим вызывает перевод перевода строки с возвратом каретки. The value returned by ftell and _ftelli64 may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-line feed translation. Используйте ftell с fseek или _ftelli64 с _fseeki64 , чтобы правильно вернуться к расположениям файлов. Use ftell with fseek or _ftelli64 with _fseeki64 to return to file locations correctly. При возникновении ошибки ftell и _ftelli64 вызывают обработчик недопустимого параметра, как описано в разделе Проверка параметров. On error, ftell and _ftelli64 invoke the invalid parameter handler, as described in Parameter Validation. Если выполнение может быть продолжено, эти функции возвращают-1L и применяют к одной из двух констант, определенных в начале. Высоты. If execution is allowed to continue, these functions return -1L and set errno to one of two constants, defined in ERRNO.H. Константа значение EBADF означает, что аргумент потока не является допустимым значением указателя файла или не ссылается на открытый файл. The EBADF constant means the stream argument is not a valid file pointer value or does not refer to an open file. Еинвал означает, что функции был передан недопустимый аргумент потока . EINVAL means an invalid stream argument was passed to the function. На устройствах, которые не могут выполнять поиск (например, терминалы и принтеры) или если поток не ссылается на открытый файл, возвращаемое значение не определено. On devices incapable of seeking (such as terminals and printers), or when stream does not refer to an open file, the return value is undefined.

Дополнительные сведения об этих и других кодах возврата см. в разделе _doserrno, errno, _sys_errlist и _sys_nerr. See _doserrno, errno, _sys_errlist, and _sys_nerr for more information on these, and other, return codes.

Примечания Remarks

Функции ftell и _ftelli64 извлекают текущую позиции указателя файла (при наличии), связанного с потоком. The ftell and _ftelli64 functions retrieve the current position of the file pointer (if any) associated with stream. Позиция представляется как смещение относительно начала потока. The position is expressed as an offset relative to the beginning of the stream.

Обратите внимание, что когда файл открыт для добавления данных, текущее положение в файле определяется последней операцией ввода-вывода, а не тем, где должна быть произведена следующая запись. Note that when a file is opened for appending data, the current file position is determined by the last I/O operation, not by where the next write would occur. Например, если файл открыт для добавления и последней операцией было чтение, положением в файле является место, где должна начинаться следующая операция чтения, а не следующая операция записи. For example, if a file is opened for an append and the last operation was a read, the file position is the point where the next read operation would start, not where the next write would start. (Если файл открыт для добавления, положение в файле перемещается в конец файла перед любой операцией записи.) Если в открытом для добавления файле еще не производилась ни одна операция ввода-вывода, то положением в файле является начало файла. (When a file is opened for appending, the file position is moved to end of file before any write operation.) If no I/O operation has yet occurred on a file opened for appending, the file position is the beginning of the file.

Илон Маск рекомендует:  Что такое код nl_langinfo

В текстовом режиме при вводе CTRL+Z интерпретируется как символ конца файла. In text mode, CTRL+Z is interpreted as an end-of-file character on input. В файлах, открытых для операций чтения и записи, fopen и все связанные подпрограммы проверяют CTRL + Z в конце файла и удаляют его, если это возможно. In files opened for reading/writing, fopen and all related routines check for a CTRL+Z at the end of the file and remove it if possible. Это делается потому, что использование сочетания ftell и fseek или _ftelli64 и _fseeki64для перемещения внутри файла, заканчивающегося клавишами CTRL + Z, может вызвать неправильное поведение ftell или _ftelli64 в конце File. This is done because using the combination of ftell and fseek or _ftelli64 and _fseeki64, to move within a file that ends with a CTRL+Z may cause ftell or _ftelli64 to behave improperly near the end of the file.

Во время выполнения функция блокирует вызывающий поток, поэтому она потокобезопасна. This function locks the calling thread during execution and is therefore thread-safe. Сведения о версии без блокировки см. в разделе _ftell_nolock. For a non-locking version, see _ftell_nolock.

Требования Requirements

Функция Function Обязательный заголовок Required header Необязательные заголовки Optional headers
ftell ftell
_ftelli64 _ftelli64

Дополнительные сведения о совместимости см. в разделе Совместимость. For additional compatibility information, see Compatibility.

Бинарные файлы

Бинарные файлы

Т екстовые файлы хранят данные в виде текста (sic!). Это значит, что если, например, мы записываем целое число 12345678 в файл, то записывается 8 символов, а это 8 байт данных, несмотря на то, что число помещается в целый тип. Кроме того, вывод и ввод данных является форматированным, то есть каждый раз, когда мы считываем число из файла или записываем в файл происходит трансформация числа в строку или обратно. Это затратные операции, которых можно избежать.

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

Выполните программу и посмотрите содержимое файла output.bin. Число, которое ввёл пользователь записывается в файл непосредственно в бинарном виде. Можете открыть файл в любом редакторе, поддерживающем представление в шестнадцатеричном виде (Total Commander, Far) и убедиться в этом.

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

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

Запись в бинарный файл объекта похожа на его отображение: берутся данные из оперативной памяти и пишутся как есть. Для считывания используется функция fread

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

fseek

Одной из важных функций для работы с бинарными файлами является функция fseek

Эта функция устанавливает указатель позиции, ассоциированный с потоком, на новое положение. Индикатор позиции указывает, на каком месте в файле мы остановились. Когда мы открываем файл, позиция равна 0. Каждый раз, записывая байт данных, указатель позиции сдвигается на единицу вперёд.
fseek принимает в качестве аргументов указатель на поток и сдвиг в offset байт относительно origin. origin может принимать три значения

  • SEEK_SET — начало файла
  • SEEK_CUR — текущее положение файла
  • SEEK_END — конец файла. К сожалению, стандартом не определено, что такое конец файла, поэтому полагаться на эту функцию нельзя.

В случае удачной работы функция возвращает 0.

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

Вместо этого можно также использовать функцию rewind, которая перемещает индикатор позиции в начало.

В си определён специальный тип fpos_t, который используется для хранения позиции индикатора позиции в файле.
Функция

используется для того, чтобы назначить переменной pos текущее положение. Функция

используется для перевода указателя в позицию, которая хранится в переменной pos. Обе функции в случае удачного завершения возвращают ноль.

возвращает текущее положение индикатора относительно начала файла. Для бинарных файлов — это число байт, для текстовых не определено (если текстовый файл состоит из однобайтовых символов, то также число байт).

Рассмотрим пример: пользователь вводит числа. Первые 4 байта файла: целое, которое обозначает, сколько чисел было введено. После того, как пользователь прекращает вводить числа, мы перемещаемся в начало файла и записываем туда число введённых элементов.

Вторая программа сначала считывает количество записанных чисел, а потом считывает и выводит числа по порядку.

Примеры

1. Имеется бинарный файл размером 10*sizeof(int) байт. Пользователь вводит номер ячейки, после чего в неё записывает число. После каждой операции выводятся все числа. Сначала пытаемся открыть файл в режиме чтения и записи. Если это не удаётся, то пробуем создать файл, если удаётся создать файл, то повторяем попытку открыть файл для чтения и записи.

2. Пишем слова в бинарный файл. Формат такой — сначало число букв, потом само слово без нулевого символа. Ели длина слова равна нулю, то больше слов нет. Сначала запрашиваем слова у пользователя, потом считываем обратно.

3. Задача — считать данные из текстового файла и записать их в бинарный. Для решения зачи создадим функцию обёртку. Она будет принимать имя файла, режим доступа, функцию, которую необходимо выполнить, если файл был удачно открыт и аргументы этой функции. Так как аргументов может быть много и они могут быть разного типа, то их можно передавать в качестве указателя на структуру. После выполнения функции файл закрывается. Таким образом, нет необходимости думать об освобождении ресурсов.

4. Функция saveInt32Array позволяет сохранить массив типа int32_t в файл. Обратная ей loadInt32Array считывает массив обратно. Функция loadInt32Array сначала инициализирует переданный ей массив, поэтому мы должны передавать указатель на указатель; кроме того, она записывает считанный размер массива в переданный параметр size, из-за чего он передаётся как указатель.

5. Создание таблицы поиска. Для ускорения работы программы вместо вычисления функции можно произвести сначала вычисление значений функции на интервале с определённой точностью, после чего брать значения уже из таблицы. Программа сначала производит табулирование функции с заданными параметрами и сохраняет его в файл, затем подгружает предвычисленный массив, который уже используется для определения значений. В этой программе все функции возвращают переменную типа Result, которая хранит номер ошибки. Если функция отработала без проблем, то она возвращает Ok (0).

6. У нас имеются две структуры. Первая PersonKey хранит логин, пароль, id пользователя и поле offset. Вторая структура PersonInfo хранит имя и фамилию пользователя и его возраст. Первые структуры записываются в бинарный файл keys.bin, вторые структуры в бинарный файл values.bin. Поле offset определяет положение соответствующей информации о пользователе во втором файле. Таким образом, получив PersonKey из первого файла, по полю offset можно извлечь из второго файла связанную с данным ключом информацию.

Зачем так делать? Это выгодно в том случае, если структура PersonInfo имеет большой размер. Извлекать массив маленьких структур из файла не накладно, а когда нам понадобится большая структура, её можно извлечь по уже известному адресу в файле.

Как получить текущую позицию в файле?

Как получить текущую позицию в файле?

int main(vo ,tell(stream)); close(stream); return 0; >

Под SCO работал tell(stream) в linux такой ф-ции нет. P.S. ftell + fopen не предлагать .

Re: Как получить текущую позицию в файле?

> P.S. ftell не предлагать.
А что не устраивает?
P.S.: отформатировать текст было бы классно?

Re: Как получить текущую позицию в файле?

open вобще-то возвращает дескриптор файла (int), а не FILE *.

А узнать позицию используя ядреные, а не libc-шные функции можено так lseek(fd, 0, SEEK_CUR).

Re: Как получить текущую позицию в файле?

С переносами строк .
Как получить текущую позицию в файле?

int main(void)
<
FILE *stream;
stream = open(«./DUMMY.FIL»,O_RDWR);
write(stream,»Это тест»,15);
// printf(«Указатель файла: %ld\n»,tell(stream));
close(stream);
return 0;
>

Под SCO работал tell(stream) в linux такой ф-ции нет.
P.S. ftell + fopen не предлагать .

Re: Как получить текущую позицию в файле?

>open вобще-то возвращает дескриптор файла (int), а не FILE *.

Это из примера по ftell осталось

>А узнать позицию используя ядреные, а не libc-шные функции можено так lseek(fd, 0, SEEK_CUR).

А за это спасибо, как я сам не додумался .

Re: Как получить текущую позицию в файле?

Не знаю правильно это или нет, но я юзаю `ftello`

Позиционирование в файле

В коде есть две подпрограммы: а и b . Каждая подпрограмма должна работать с одним и тем же файлом. Соответственно, в зависимости от вызванной функции должно меняться позиционирование в файле. Как это правильно сделать? Подойдет ли здесь функция fseek ? Можно ли менять позиционирование прямо по строкам?

1 ответ 1

Про позиционирование в текстовых файлах вам уже написали в комментарии — @AnT. Впрочем, ваши вызовы fseek выставляют позицию в файле только на начало или конец, и тут все будет работать нормально (в смысле — выставлять указатель, куда надо, потому как у MS сказано: корректно работает поиск со смещением 0 относительно любого значения origin). Кроме одного вызова — о нем далее:

Здесь вы пытаетесь выйти за пределы файла — на 8 байт дальше, чем он заканчивается. Это в принципе не запрещено (см. там же), но пустые места забьются нулями. Это точно желательное для вас поведение?

Не стоит закрывать не открытый файл. Вот что сказано в документации:

Функция fclose закрывает stream. Если параметр stream имеет значение NULL, вызывается обработчик недопустимого параметра, как описано в разделе Проверка параметров. Если выполнение может быть продолжено, то fclose устанавливает errno в EINVAL и возвращает EOF. Рекомендуется, всегда проверять указатель stream до вызова этой функции.

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

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

ftell

(PHP 4, PHP 5, PHP 7)

ftell — Возвращает текущую позицию указателя чтения/записи файла

Описание

Возвращает позицию файлового указателя handle .

Список параметров

Файловый указатель должен быть действующим указателем на поток, открытый функциями fopen() или popen() . ftell() возвращает неопределённые результаты для потоков, разрешающих только запись в конец (открытых с флагом «a»).

Возвращаемые значения

Возвращает позицию файлового указателя handle в виде целого числа, то есть его смещение в файловом потоке.

При возникновении ошибки возвращает FALSE .

Замечание: Так как тип integer в PHP является целым числом со знаком и многие платформы используют 32-х битные целые числа, то некоторые функции файловых систем могут возвращать неожиданные результаты для файлов размером больше 2 Гб.

Примеры

Пример #1 Пример использования функции ftell()

// открываем файл и читаем немного данных
$fp = fopen ( «/etc/passwd» , «r» );
$data = fgets ( $fp , 12 );

// где же мы ?
echo ftell ( $fp ); // 11

Смотрите также

  • fopen() — Открывает файл или URL
  • popen() — Открывает файловый указатель процесса
  • fseek() — Устанавливает смещение в файловом указателе
  • rewind() — Сбрасывает курсор файлового указателя

User Contributed Notes 6 notes

When opening a file for reading and writing via fopen(‘file’,’a+’) the file pointer should be at the end of the file. However ftell() returns int(0) even if the file is not empty. Also it seems that there is two pointers, first for reading and second for writing, because it acts differently on first operation (reading or writing).

Ftell дать позицию в файле

«Двоичный файл это:

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

б).файл, в котором данные представлены в двоичной системе счисления»

Модель двоичного файла

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

· физическая память имеет байтную структуру – единицей адресации является байт;

· любая переменная занимает фиксированное количество байтов, определяемое ее типом. Операция sizeof возвращает эту размерность ;

int fread (void *buf, int size, int nrec, FILE *fd);

int fwrite (void *buf, int size, int nrec, FILE *fd);

Чтобы воспользоваться этими функциями, необходимо обеспечить преобразования переменных к «массиву байтов», используя указатели для задания адресов и операцию sizeof для вычисления размерности:

// Прочитать целую переменную и следующий за ней

// динамический массив из n переменных типа double

int n ; // в целой переменной – размерность массива

fread (& n , sizeof ( int ),1, fd ); // указатель на переменную int

double *pd = new double[n];

fread(pd, sizeof(double),n,fd); // преобразование к void* — неявное

Произвольный доступ базируется на понятии адреса в двоичном файле. Поскольку на физическом уровне двоичный файл представляется как «неограниченно растущий» массив байтов, то под адресом понимается порядковый номер байта, начиная с 0.

В Си для представления адресов используются переменные типа long , на которые можно распространить известное понятие указатель — указатель в файле. Такая образная аналогия вполне уместна и позволяет с общих позиций рассматривать структуры данных, размещенные в файлах. Указатель в файле не является типизированным, его тип никак не связан с типом адресуемых данных и он рассматривается как «чистый» физический адрес в файле.

Замечание: текущая позиция в файле является адресом размещения переменной в нем, но получить этот адрес можно перед, и не после ее чтения оттуда.

#define FNULL -1L

#define SEEK_SET 0 // Относительно начала файла

// начало файла — позиция 0

#define SEEK_CUR 1 // Относительно текущей позиции,

// >0 — вперед, // Относительно конца файла

// (значение pos — отрицательное)

fseek(fl,0L,SEEK_END); // Установить позицию на конец файла

fsize = ftell(fd); // Прочитать значение текущей позиции

fseek ( fd ,100, SEEK _ SET ); // По адресу 100 находится смещение

fread (& P , sizeof ( long ),1, fd ); // Читается P =46, после чтения текущая позиция

fseek(fd,i,SEEK_CUR); // 100+sizeof(long)=104, позиционирование 104+46=150

Замечание: введя понятие произвольного доступа по адресу в файле, мы не ответили на главные вопросы: а откуда взять эти адреса и как размещаются данные в файле (распределяется память). Эти вопросы – к технологии программирования.

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

// Открыть существующий как двоичный для чтения и записи

// Создать новый как двоичный для записи и чтения

Дамп двоичного файла

При отладке программ, работающих с двоичными файлами, иногда сложно установить, какой фрагмент – запись или чтение – содержит ошибку. Аналогично, при чтении уже известного формата необходимо проверять, насколько правильно читаются данные. Здесь не обойтись без навыков чтения дампа – двоичного содержимого файла. Для этого нам придется вспомнить основы представления базовых типов данных в памяти (см. 1.3). Естественно, что все данные и адреса присутствуют в шестнадцатеричной системе счисления.

// Формирование ДАМПА для чтения файла

fwrite(&p,sizeof(long),1,fd); // Занять место под указатель

fwrite(&k,1,1,fd); // Записать один байт — счетчик

fwrite(A,sizeof(short),k,fd); // Записать массив коротких целых (2B)

p=ftell(fd); // Получить значение указателя

fwrite(&m,1,1,fd); // Записать один байт — счетчик

fwrite(B,sizeof(int),m,fd); // Записать массив целых

fseek(fd,0,SEEK_SET); // К началу файла

fwrite(&p,sizeof(long),1,fd); // Обновить указатель на второй массив

Чтобы теперь «увидеть» в дампе то, что мы записали, нужно учесть следующее:

· дамп выводится побайтно, один байт представлен двумя шестнадцатеричными цифрами;

· в каждой строке дампа – 16 байтов. Слева находится адрес строки (естественно, шестнадцатеричный). На рисунке для удобства обозначена младшая цифра адреса каждого байта;

· все данные пишутся «младшими байтами вперед». Т.е. для получения значения машинного слова байты надо переписать в обратном порядке;

· для данных другого типа необходим перевод в десятичную систему счисления, а также учет их размерности в соответствии с их типом.

Управление данными в двоичном файле

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

// Добавить в файл вещественную переменную

fseek ( fd ,0 L , SEEK _ END ); // Позиционироваться на конец файла

fwrite (& b , sizeof ( double ),1, fd ); // Записать переменную как массив байтов

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

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

// Обновить счетчик в двоичном файле

fseek(fd,pos,SEEK_SET); // Читать счетчик

a++; // Увеличить в памяти

fseek ( fd , pos , SEEK _ SET ); // Записать обратно по тому же адресу

· структуры данных в памяти и в файле принципиально различаются. Например, дерево (данные в вершинах и связи) можно сохранить в последовательном потоке (файле) в виде рекурсивного саморазворачивающегося формата (см. 8.2). В этом случае используется полная загрузка/сохранение структуры данных в виде единой операции;

· в памяти могут создаваться образы наиболее важных (часто используемых, управляющих) элементов структуры данных из двоичного файла. Например, при наличии в файле массива указателей на строки в памяти создается «образ» заголовка файла и массива указателей, а сами строки читаются «по необходимости». Такой способ создания и загрузки частичного образа структуры данных предполагает сеансовый режим работы с файлом: частичный образ создается в памяти при открытии файла. Здесь также возникает проблема соответствия образа оригиналу и обновления последнего;

Запись — стандартная единица хранения данных в файле Запись — это единица хранения, которую получает внешний пользователь, «прикладная» часть структуры данных, находящейся в файле. Кроме нее в файле присутствует в том или ином виде «системная» часть, которая обеспечивает упорядоченность, ускоренный поиск и другой необходимый сервис для работы с записями. В Си под записью понимается технологическая единица, поддерживаемая программой, поскольку физическая модель файла представлена более низким уровнем.

Запись фиксированной длины — все записи файла представляют собой переменные одного типа и имеют фиксированную для данного файла размерность. Обычно файл записей фиксированной длины представляет собой массив переменных одного типа.

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

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

Последовательный доступ — файл по своей физической организации (устройство ввода-вывода) или по характеру структуры данных допускает просмотр записей в последовательном порядке. При отсутствии операций позиционирования записи в файле извлекаются в режиме последовательного доступа.

Способы организации данных в файлах

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

Предлагаемая классификация базируется на различии способов доступа к данным. Фактически вопрос сводится к способу получения их адреса. Их известно несколько:

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

· адрес вычисляется, исходя из количества и размерности переменных. Сюда относятся массивы и более сложные табличные структуры данных, размерности которых хранятся в них самих (параметризация).

Позиционирование в текстовом файле

Текстовые файлы являются по своей природе файлами последовательного доступа. Единственным исключением из этого правила является позиционирование (возврат) к уже прочитанному фрагменту текста при помощи функции fseek. Но для этого необходимо при первоначальном последовательном просмотре файла определить текущую позицию этого фрагмента в файле функций ftell, вызвав ее перед функцией чтения. Проиллюстрирует этот принцип рядом примеров.

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

void more(FILE *fd)<

long pp ; // Текущая позиция фрагмента повторения

char c ; int n =0; // Количество повторов

pp = ftell ( fd ); // Запомнить текущую позицию

if (!isdigit(c)) break; //

n = n *10+ c -‘0’; // Накопление константы

if ( n ==0) n =1; // Отсутствие константы — повторить 1 раз

fseek ( fd , pp , SEEK _ SET ); // Вернуться на начало

while ((c=getc(fd)) !=EOF && c!=’)’)<

if ( c =='(‘) more ( fd ); // Вложенный фрагмент —

else // рекурсивный вызов после ‘(‘

putchar ( c ); // Перечитать фрагмент до ‘)’

vo ); more(fd); fclose(fd); >

//—— Вывод текста с заданной страницы

FILE *fd; char name[30]=»94.txt» , str[80];

int i,n,NP; // Количество страниц в файле

long *POS; // Массив адресов начала страниц в файле

if ((fd=fopen(name,»r»))==NULL) return;

NP=n/20; if (n%20!=0) NP++; // Кол-во строк — кол-во-страниц

fseek(fd,0,SEEK_SET); // Вернуться в начало файла

POS=new long[NP]; // Динамический массив «закладок»

for (n=0; n // Просмотр страниц файла

POS[n]=ftell(fd); // Запомнить начало страницы

for (i=0; i // Чтение строк страницы

break; // Конец файла — выход из цикла

if (i // Неполная страница — выход

printf(«page number(0..%d):»,NP-1); scanf(«%d»,&n);

fseek(fd,POS[n],SEEK_SET); // Позиционироваться на страницу

for (i=0; i // Повторное чтение страницы

if (fgets(str,80,fd)==NULL) break;

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

· создать в программе стек, содержащий «точки возврата», которые также являются адресами в тексте;

· если при чтении текста встречается «вызов» функции, то после его прочтения сохранить в стеке текущий адрес в текстовом файле, получив его через ftell . Затем найти в таблице имя вызываемой функции и позиционироваться к ее началу с помощью fseek ;

· если при чтении обнаруживается завершение функции, то из стека извлекается адрес (точка возврата) и к ней производится позиционирование с помощью fseek .

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

Насколько же можно продвинуться в прямом доступе в текстовом файле? В принципе, никто не мешает нам записывать (обновлять) фрагменты текста, но при условии, что их размер будет точно совпадать с имеющимся. Следующий пример показывает, как извлечь из текстового файла числовое значение 6-разрядного счетчика и, увеличив, аккуратно «вписать» обратно.

//—— «Микрохирургическое» исправление счетчика

FILE *fd; char cc, name[30]=»94-03.txt»;

long POS; int cnt;

if ((fd=fopen(name,»r+w»))==NULL) return;

POS=ftell(fd); // Запомнить адрес символа

if ((cc=getc(fd))==EOF) break;

if (cc>=’0′ && cc // Прочитана цифра

fseek(fd,POS,SEEK_SET); // Вернуться на 1 символ

fscanf(fd,»%d»,&cnt); // и прочитать счетчик — 6 символов

cnt++; // Увеличить счетчик

fseek(fd,POS,SEEK_SET); // Вернуться на начало счетчика

fprintf(fd,»%06d»,cnt); // и записать счетчик — 6 символов

Лабораторный практикум

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

1. Сортировка строк файла по длине и по алфавиту и вывод результата в отдельный файл.

2. Программа-интерпретатор текста. Текстовый файл разбит на именованные модули. Каждый модуль может иметь вызовы других текстовых модулей. Требуется вывести текст модуля main с включением текстов других модулей в порядке вызова:

Произвольные строки модуля текста ааа

Произвольные строки текста

#aaa // Вызов модуля текста с именем aaa

Произвольные строки текста

Основной текст с вызовами других модулей

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

4. Программа — интерпретатор текста, включающего фрагменты следующего вида :

При просмотре файла программа выводит его текст, текст фрагментов «#repeat — #end» выводится указанное количество раз. Фрагменты могут быть вложенными.

5. Программа просмотра блочной структуры Си-программы с командами вывода текущего блока, входа в n-ый по счету вложенный блок и выхода в блок верхнего уровня.

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

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

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

9. Программа составляет словарь терминов. Каждый термин — слово, записанное большими (строчными) буквами. Программа запоминает каждый термин и указатель на строку, в которой он встречается. Кроме того, программа позволяет просматривать текст в обоих направлениях построчно и при выборе текущей строки ищет в ней термин и позиционируется к нему.

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

11. Программа составляет «оглавление» текстового файла путем поиска и запоминания позиций строк вида «5.7.6 Позиционирование в текстовом файле». Затем программа составляет меню, с помощью которого позиционируется в начало соответствующих разделов и пунктов с прокруткой текста в обоих направлениях.

12. Программа составляет словарь функций Си-программы. Затем программа составляет меню, с помощью которого позиционируется в начало соответствующих функций. (Функцию достаточно идентифицировать по фрагменту вида «идентификатор(. » вне фигурных скобок).

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

14. Программа ищет в тексте Си-программы самый внутренний блок (для простоты начало и конец блока располагаются в отдельных строчках), присваивает ему номер и «выкусывает» основного текста, заменяя его ссылкой на этот номер. Затем по заданному номеру блока производится его вывод на экран, в тексте блока при этом должна присутствовать строка вида «#БЛОК nnn » при наличии вложенного блока. (Процедуру «выкусывания» блоков рекомендуется реализовать при помощи «выкусывания» указателей на строки вложенного блока в файле и замене их на отрицательное число — n , где n -номер, присвоенный блоку).

15. Программа сортировки файла по длине предложений и вывода результата в отдельный файл. При выводе каждое предложение следует переформатировать так, чтобы оно начиналось с отдельной строки и располагалось в строках размером не более 60 символов.

Ftell дать позицию в файле

118 просмотра

1 ответ

117 Репутация автора

На этот вопрос уже есть ответ здесь:

У меня есть текстовый файл с именем myfile.txt, который гласит:

Всякий раз, когда встречается \ n, ftell пропускает одно значение, например, оно пропустило значение 7. Почему это так? Пожалуйста, объясните мне подробно, я хочу знать.

Ответы (1)

1 плюс

55361 Репутация автора

Проблема заключается в символе новой строки, который в Windows есть \r\n ( возврат каретки Windows \ r \ n состоит из двух символов или одного символа? ).

Попробуйте изменить это:

где b для двоичного режима.

Двоичный режим имеет значение в Windows, где текстовый режим отображает двухсимвольный возврат каретки, последовательность перевода строки на один символ новой строки. Примечание. В Linux нет необходимости в отображении.

Илон Маск рекомендует:  Что такое код swf_defineline
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL