В C нет переносимого, соответствующего стандарту способа заранее знать, как могут считываться байты из потока FILE
.
Во-первых, поток может даже не быть доступным - он может быть трубой или клеммой или даже разъемом. В таких потоках, когда вы читаете входные данные, они исчезают, и больше никогда не будут прочитаны. Вы можете вернуть sh одно значение char
, но этого недостаточно, чтобы узнать, сколько данных осталось прочитать, или перечитать весь поток.
И даже если поток файл, который вы можете искать, вы не можете использовать fseek()
/ ftell()
в переносимом, строго соответствующем C коде, чтобы узнать, насколько велик файл.
Если это двоичный поток, вы не можете использовать fseek()
для поиска в конце файла - это явно неопределенное поведение в соответствии со C стандартом :
... Бинарный поток не требуется Значительно поддерживает fseek
вызовы со значением откуда SEEK_END
.
Сноска 268 даже говорит :
Установка индикатора положения файла в конец файла, как и в fseek(file, 0, SEEK_END)
, имеет неопределенное поведение для двоичного потока ...
Таким образом, вы не можете использовать fseek()
в двоичном потоке для переноса.
И вы не можете использовать ftell()
, чтобы получить количество байтов для текстового потока. По стандарт C снова :
Для текстового потока его индикатор положения файла содержит неопределенную информацию, используемую функцией fseek для возврата индикатора положения файла для потока на свою позицию во время полного вызова; разница между двумя такими возвращаемыми значениями не обязательно является значимым показателем количества написанных или прочитанных символов.
Существуют системы, в которых значение, возвращаемое из ftell()
, не имеет ничего общего с числом байтов.
Единственный переносимый, соответствующий способ узнать, сколько байтов вы можете прочитать из потока это на самом деле их читать, и вы не можете полагаться на возможность их чтения снова.
Если вы хотите прочитать весь поток в память, вам необходимо постоянно перераспределять память или использовать некоторые другие динамические * Схема 1090 *.
Это очень неэффективный, но переносимый и строго согласованный способ считывания всего содержимого потока в память (все файлы проверки ошибок и заголовочные файлы опущены для ясности алгоритма и для сохранения вертикальной полосы прокрутки). от появления - это действительно нуждается в проверке ошибок и потребует правильных заголовочных файлов):
// get input stream with `fopen()` or some other manner
FILE *input = ...
size_t count = 0;
char *data = NULL;
for ( ;; )
{
int c = fgetc( input );
if ( c == EOF )
{
break;
}
data = realloc( data, count + 1 );
data[ count ] = c;
count++;
}
// optional - terminate the data with a '\0'
// to treat the data as a C-style string
data = realloc( data, count + 1 );
data[ count ] = '\0';
count++;
Это будет работать независимо от того, какой поток.
В системе типа POSIX, такой как Linux, вы можете использовать fileno()
и fstat()
, чтобы получить размер файла (опять же, все проверки на ошибки и заголовочные файлы опущены d):
char *data = NULL;
FILE *input = ...
int fd = fileno( input );
struct stat sb;
fstat( fd, &sb );
if ( S_ISREG( sb.st_mode ) )
{
// sb.st_size + 1 for C-style string
char *data = malloc( sb.st_size + 1 );
data[ sb.st_size ] = '\0';
}
// now if data is not NULL you can read into the buffer data points to
// if data is NULL, see above code to read char-by-char
// this tries to read the entire stream in one call to fread()
// there are a lot of other ways to do this
size_t totalRead = 0;
while ( totalRead < sb.st_size )
{
size_t bytesRead = fread( data + totalRead, 1, sb.st_size - totalRead, input );
totalRead += bytesRead;
}
Вышеуказанное может работать и на Windows. Вы можете получить некоторые предупреждения компилятора или использовать _fileno()
, _fstat()
и struct _stat
вместо этого тоже. *
Вам также может понадобиться определить макрос S_ISREG()
для Windows:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
* это _fileno()
, _fstat()
и struct _stat
без подчеркивания гиперссылки. munge.