Чтение c файла построчно с помощью fgetc () - PullRequest
6 голосов
/ 27 ноября 2010

Вот как я это сделал, но я не уверен, что это предпочтительная идиома:

FILE *fp = fopen(argv[0], "r");
// handle fopen() returning NULL

while (!feof(fp)) {
    char buffer[80]; // statically allocated, may replace this later with some more sophisticated approach
    int num_chars = 0;

    for (int ch = fgetc(fp); ch != EOF && ch != '\n'; ch = fgetc()) {
        buffer[num_chars++] = ch;
    }

    // null-terminate the string
    buffer[num_chars] = '\0';

    printf("%s\n", buffer);
}

Это нормально, есть предложения по улучшению?

Ответы [ 6 ]

13 голосов
/ 28 ноября 2010

Если вы не собираетесь использовать fgets() (возможно, потому что вы хотите удалить символ новой строки, или вы хотите иметь дело с "\r", "\n" или "\r\n" окончаниями строк, или вы хотите знать, сколькосимволы были прочитаны), вы можете использовать это как функцию скелета:

int get_line(FILE *fp, char *buffer, size_t buflen)
{
    char *end = buffer + buflen - 1; /* Allow space for null terminator */
    char *dst = buffer;
    int c;
    while ((c = getc(fp)) != EOF && c != '\n' && dst < end)
        *dst++ = c;
    *dst = '\0';
    return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}

Он распознает только символ новой строки в качестве конца строки;это бросает перевод строки.Это не переполняет буфер;он не сбрасывает лишние символы, поэтому, если он вызван для чтения очень длинной строки, он будет читать строку кусками;возвращает количество прочитанных символов.Если вам нужно различить переполнение и строку, которая оказывается длиной буфера - 1, то вам, вероятно, нужно сохранить символ новой строки - с соответствующими изменениями в коде:

int get_line(FILE *fp, char *buffer, size_t buflen)
{
    char *end = buffer + buflen - 1; /* Allow space for null terminator */
    char *dst = buffer;
    int c;
    while ((c = getc(fp)) != EOF && dst < end)
    {
        if ((*dst++ = c) == '\n')
            break;
    }
    *dst = '\0';
    return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}

Их бесконечно много.второстепенные варианты, такие как отбрасывание лишних символов, если строка должна быть обрезана.Если вы хотите обработать DOS, (старые) окончания строк Mac или Unix, позаимствуйте лист из кода CSV из "Практика программирования" Kernighan & Pike (отличная книга) и используйте:

static int endofline(FILE *ifp, int c)
{
    int eol = (c == '\r' || c == '\n');
    if (c == '\r')
    {
        c = getc(ifp);
        if (c != '\n' && c != EOF)
            ungetc(c, ifp);
    }
    return(eol);
}

Затем вы можете использовать это вместо теста c != '\n':

int get_line(FILE *fp, char *buffer, size_t buflen)
{
    char *end = buffer + buflen - 1; /* Allow space for null terminator */
    char *dst = buffer;
    int c;
    while ((c = getc(fp)) != EOF && !endofline(fp, c) && dst < end)
        *dst++ = c;
    *dst = '\0';
    return((c == EOF && dst == buffer) ? EOF : dst - buffer);
}

Другой альтернативный способ работы со всем процессом - использование fread()и fwrite():

void copy_file(FILE *in, FILE *out)
{
    char buffer[4096];
    size_t nbytes;
    while ((nbytes = fread(buffer, sizeof(char), sizeof(buffer), in)) != 0)
    {
        if (fwrite(buffer, sizeof(char), nbytes, out) != nbytes)
            err_error("Failed to write %zu bytes\n", nbytes);
    }
}

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

copy_file(fp, stdout);
1 голос
/ 28 ноября 2010

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

Заменив ваш пример похожим, но другим простым fgets(), вы «потеряете» переменную num_chars.

fgets(buffer, sizeof buffer, stdin);
fputs(buffer, stdout); /* buffer contains a '\n' */

Если вам нужно удалить последний '\ n'

buffer[0] = 0;
if (!fgets(buffer, sizeof buffer, stdin)) /* error or eof */;
num_chars = strlen(buffer);
if (num_chars && (buffer[num_chars - 1] == '\n')) buffer[--num_chars] = 0;
puts(buffer); /* add a '\n' to output */

Если строки действительно огромные (например, 42 мегабайта), вам лучше читать символ за символом и вести счет с num_chars, чем сначала использовать fgets и strlen позже.

1 голос
/ 27 ноября 2010

Вы рискуете переполнением буфера, если пользователь вводит 80 или более символов.

Я с ThiefMaster: вы должны использовать fgets() вместо этого.Считайте ввод в буфер, который больше любого допустимого ввода, а затем проверьте, что последний символ является новой строкой.

1 голос
/ 27 ноября 2010

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

 fgets (buffer, BUFFER_SIZE, fp);

Обратите внимание, что fgets будет читать, пока не будет достигнута новая строка или EOF (или, конечно, буфер заполнен).Символ новой строки "\ n" также добавляется к строке при чтении из файла.Также добавляется нулевой символ.

fgets возвращает:

В случае успеха функция возвращает тот же параметр str.
Если конецОбнаружен файл, символы не прочитаны, содержимое str остается неизменным и возвращается нулевой указатель.
Если возникает ошибка, возвращается нулевой указатель.
Используйте ferror или feof, чтобы проверить, есть ли ошибкапроизошло или достигнут конец файла.

0 голосов
/ 22 марта 2011
#include<stdio.h>
void main()
{
    FILE *fp;
    char c;
    int ch=0,w=0,l=0;
    fp=fopen("c:\read.txt","w");
    clrscr();
    if(fp==NULL)
    {
        printf("\n\n\tDOES NOT EXIXST");
        getch();
        exit(0);
    }
    while(!feof(fp))
    {
        c=fgetc(fp);

        ch++;
        if(c==' ')
        {
            w++;
        }
        if(c=='\n')
        {
            l++;
            w++;
        }
    }

    printf("\n\n\tTOTAL CHAR = %d\n\n\tTOTAL WORDS = %d\n\n\tTOTAL LINES = %d",ch,w,l);

}
0 голосов
/ 28 ноября 2010

Без ограничения размера строки и строго C89 (ваш код только C99), например:

FILE *fp = fopen(argv[0], "r");
size_t len=1;
char c, *buffer=calloc(1,1);
/* handle fopen() returning NULL*/
while( c=fgetc(fp),!feof(fp) )
  if( c=='\n' )
  {
    puts(buffer);
    len=1;
    *buffer=0;
  }
  else
    strncat(buffer=realloc(buffer,++len),&c,1); /* check for NULL needed */

puts(buffer);
free(buffer);
fclose(fp);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...