Всегда ли fgets() завершает буфер char с помощью\0?

Всегда ли fgets() завершает буфер char с \0, даже если EOF уже достигнут? Похоже, что это так (это, безусловно, имеет место в реализации, представленной в книге ANSI K & R), но я думал, что попрошу вас убедиться.

Я предполагаю, что этот вопрос применим к другим подобным функциям, таким как gets().

EDIT: Я знаю, что \0 добавляется во время "нормальных" обстоятельств, мой вопрос нацелен на EOF или условия ошибки. Например:

FILE *fp;
char b[128];
/* ... */
if (feof(fp)) {
    /* is \0 appended after EACH of these calls? */
    fgets(b, 128, fp);
    fgets(b, 128, fp);
    fgets(b, 128, fp);
}

Ответ 1

Никогда не используйте get!!

    7.19.7.2 The fgets function
    Synopsis
1           #include <stdio.h>
            char *fgets(char * restrict s, int n,
                 FILE * restrict stream);
    Description
2   The fgets function reads at most one less than the number of characters
    specified by n from the stream pointed to by stream into the array pointed
    to by s. No additional characters are read after a new-line character
    (which is retained) or after end-of-file. A null character is written
    immediately after the last character read into the array.
    Returns
3   The fgets function returns s if successful. If end-of-file is encountered
    and no characters have been read into the array, the contents of the array
    remain unchanged and a null pointer is returned. If a read error occurs
    during the operation, the array contents are indeterminate and a null
    pointer is returned.

Итак, да, когда fgets() не возвращает NULL, целевой массив всегда имеет нулевой символ.

Если fgets() возвращает NULL, целевой массив может быть изменен и может не иметь нулевого символа. Никогда не полагайтесь на массив после получения NULL от fgets().


Добавлен пример редактирования

$ cat fgets_error.c
#include <stdio.h>

void print_buf(char *buf, size_t len) {
  int k;
  printf("%02X", buf[0]);
  for (k=1; k<len; k++) printf(" %02X", buf[k]);
}

int main(void) {
  char buf[3] = {1, 1, 1};
  char *r;

  printf("Enter CTRL+D: ");
  fflush(stdout);
  r = fgets(buf, sizeof buf, stdin);
  printf("\nfgets returned %p, buf has [", (void*)r);
  print_buf(buf, sizeof buf);
  printf("]\n");

  return 0;
}
$ ./a.out
Enter CTRL+D:
fgets returned (nil), buf has [01 01 01]
$

См? no NUL в buf:)

Ответ 2

fgets всегда добавляет '\ 0' в буфер чтения, он считывает не более size - 1 символов из потока (size является вторым параметром) из-за этого.

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

Ответ 3

man fgets:

fgets() читает не более одного символа размера из потока и сохраняет их в буфер, на который указывает s. Чтение останавливается после EOF или новой строки. Если считывается новая строка, она сохраняется в буфере. A '\ 0' сохраняется после последнего символа в буфере.

Ответ 4

Да, да. Из CPlusPlus.com

Читает символы из потока и сохраняет их как строку C в str до тех пор, пока не будут прочитаны символы (num-1) или не будет достигнута новая строка или конечный файл, в зависимости от того, что наступит раньше.

Символ новой строки заставляет fgets останавливать чтение, но считается допустимым символом и поэтому он включен в строку, скопированную на str.

Нулевой символ автоматически добавляется в str после того, как символы будут считаться сигналом конца строки C.

Ответ 5

Если вы открыли файл в двоичном режиме "rb", и если вы хотите читать "Текст за строкой", используя fgets, вы можете использовать следующий код для защиты вашего программного обеспечения от потери текста, если по ошибке текст содержит a '\ 0' байт. Но, наконец, как и другие, вы обычно не должны использовать fgets, если поток содержит "\ 0".


size_t filepos=ftell(stream);
fgets(buffer, buffersize, stream);
len=strlen(buffer);
/* now check for > len+1 since no problem if the 
   last byte is 0 */
if(ftell(stream)-filepos > len+1) 
{
    if(!len) filepos++;
    if(!fseek(stream, filepos, SEEK_SET) && len)
    {
        fread(buffer, 1, len, stream);
        buffer[len]='\0';
    }
}