需要帮助以C中的逐个字符读取文件 [英] Need help for reading a file character by character in C

查看:58
本文介绍了需要帮助以C中的逐个字符读取文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个问题,要逐个字符读取文件并用C计数

I have a question about reading a file character by character and counting it in C

下面是我的代码

void read_in(char** quotes){
    FILE *frp = fopen(IN_FILE, "r");
    
    char c;
    size_t tmp_len =0, i=0;    
    //char* tmp[100];
    //char* quotes[MAX_QUOTES];
    //char str = fgets(str, sizeof(quotes),frp);
    
    while((c=fgetc(frp)) != EOF){        
        if(frp == NULL){
            printf("File is empty!");
            fclose(frp); exit(1);
        }
        
        else{  
            if(c != '\n'){           
                printf("%c",c);
                c=fgetc(frp);
                tmp_len++;                 
            }        
        
        }
        char* tmp = (char*)calloc(tmp_len+1, sizeof(char));        
        fgets(tmp, sizeof(tmp), frp);
        strcpy((char*)quotes[i], tmp);        
        
        printf("%s\n", (char*)quotes[i]);
        
        i++;
    }
}

这行不通,但我不明白为什么.

It doesn't work but I don't understand why.

谢谢

推荐答案

从您的问题和注释中可以很明显地看出,您想将文件中的所有引号(行)读入动态分配的存储区(屏幕1),然后然后按长度对行进行排序,并输出前5条最短的行(屏幕2),将5条最短的行保存到第二个输出文件中(这部分留给您使用).读取和存储文件中的所有行并不困难-但这也不是一件容易的事.听起来很基础,但确实如此,但它要求您正确使用与持久性存储(从磁盘/存储介质读取文件)和计算机的内存子系统(RAM)进行交互所需的所有基本工具.

From your question and through the comments, it is relatively clear you want to read all quotes (lines) in a file into dynamically allocated storage (screen 1) and then sort the lines by length and output the first 5 shortest lines (screen 2) saving the 5 shortest lines to a second output file (this part is left to you). Reading and storing all lines from a file isn't difficult -- but it isn't trivial either. It sounds basic, and it is, but it requires that you use all of the basic tools needed to interface with persistent storage (reading the file from disk/storage media) and your computer's memory subsystem (RAM) -- correctly.

从文件中读取每一行并不困难,但是与C语言中的任何内容一样,它要求您注意细节.您可以使用面向字符的输入函数( fgetc() getc()等)从文件中读取内容,也可以使用 formatted-input 函数( fscanf()),您可以使用 line-directional 输入函数,例如( fgets()或POSIX getline()).从文件中读取行通常使用面向 line 的函数完成,但是使用面向字符的 方法也没有错.实际上,您可以相对容易地基于 fgetc()编写一个函数,该函数将为您从文件中读取每一行.

Reading each line from a file isn't difficult, but like anything in C, it requires you to pay attention to the details. You can read from a file using character-oriented input functions (fgetc(), getc(), etc..), you can use formatted-input functions (fscanf()) and you can use line-oriented input functions such as (fgets() or POSIX getline()). Reading lines from a file is generally done with line-oriented functions, but there is nothing wrong with using a character-oriented approach either. In fact you can relatively easily write a function based around fgetc() that will read each line from a file for you.

在琐碎的情况下,您知道文件中最长行的最大字符数,可以使用2D字符数组来存储整个文件.这样就消除了动态分配存储的需求,从而简化了流程,但有许多缺点,例如文件中的每一行都需要与文件中最长的行相同的存储空间,并且可以限制可存储文件的大小.程序堆栈的大小.使用( malloc calloc realloc )动态分配存储消除了这些缺点和效率低下的问题,使您可以将文件存储到内存的上限在您的计算机上可用.(有一些方法都可以通过使用滑动窗口技术来处理任何大小的文件,而这超出了您的需求)

In the trivial case where you know the maximum number of characters for the longest line in the file, you can use a 2D array of characters to store the entire file. This simplifies the process by eliminating the need to allocate storage dynamically, but has a number of disadvantages like each line in the file requiring the same storage as the longest line in the file, and by limiting the size of the file that can be stored to the size of your program stack. Allocating storage dynamically with (malloc, calloc, or realloc) eliminates these disadvantages and inefficiencies allowing you to store files up to the limit of the memory available on your computer. (there are methods that allow both to handle files of any size by using sliding-window techniques well beyond your needs here)

处理动态分配的内存,或逐个字符地在其中复制或存储数据并不困难.也就是说,每个分配的责任,跟踪写入每个分配的块的数据量,重新分配以调整块的大小以确保没有数据写入每个块的边界之外,然后在不再需要时释放每个分配的块--是你的,程序员.C使程序员有权使用可用内存的每个字节,并且赋予程序员正确使用内存的责任.

There is nothing difficult about handling dynamically allocated memory, or in copying or storing data within it on a character-by-character basis. That said, the responsibility for each allocation, tracking the amount of data written to each allocated block, reallocating to resize the block to ensure no data is written outside the bounds of each block and then freeing each allocated block when it is no longer needed -- is yours, the programmer. C gives the programmer the power to use each byte of memory available, and also places on the programmer the responsibility to use the memory correctly.

存储文件的基本方法很简单.您从文件中读取每一行,为每个字符分配/重新分配存储,直到遇到'\ n' EOF .要协调所有行,您需要分配一个指针块,然后为持有一行的每个内存块的地址依次分配一个指针,从而重新分配保持所有行所需的指针数.

The basic approach to storing a file is simple. You read each line from the file, allocating/reallocating storage for each character until a '\n' or EOF is encountered. To coordinate all lines, you allocate a block of pointers, and you assign the address for each block of memory holding a line to a pointer, in sequence, reallocating the number of pointers required as needed to hold all lines.

有时候一张图片真的值一千字.使用基本方法,您可以将指针声明为指针(指向什么?),以便可以分配一个包含指针的内存块,并为每个分配的行分配指针.例如,您可以声明 char ** lines; 一个 pointer-to-pointer 是指向包含指针的内存块的单个指针.然后 lines 的每个指针的类型将是 char * ,它将指向文件中持有一行的每个块,例如

Sometimes a picture really is worth 1000 words. With the basic approach you declare a pointer (to what?) a pointer so you can allocate a block of memory containing pointers to which you will assign each allocated line. For example, you could declare, char **lines; A pointer-to-pointer is a single pointer that points to a block of memory containing pointers. Then the type for each pointer for lines will be char * which will point to each block holding a line from the file, e.g.

 char **lines;
    |
    |      allocated
    |      pointers    allocated blocks holding each line
  lines --> +----+     +-----+
            | p1 | --> | cat |
            +----+     +-----+--------------------------------------+
            | p2 | --> | Four score and seven years ago our fathers |
            +----+     +-------------+------------------------------+
            | p3 | --> | programming |
            +----+     +-------------------+
            | .. |     |        ...        |
            +----+     +-------------------+
            | pn | --> | last line read |
            +----+     +----------------+

通过分配1个额外的指针并将该指针初始化为 NULL ,可以使 lines 的使用更加灵活,从而可以遍历 lines code>不知道有多少行-直到遇到 NULL ,例如

You can make lines a bit more flexible to use by allocating 1 additional pointer and initializing that pointer to NULL which allows you to iterate over lines without knowing how many lines there are -- until NULL is encountered, e.g.

            | .. |     |        ...        |
            +----+     +-------------------+
            | pn | --> | last line read |
            +----+     +----------------+
            |pn+1|     | NULL |
            +----+     +------+

虽然您可以将所有功能整合到一个功能中,以帮助学习过程(并且仅出于实用的可重用性),但通常更容易将其分解为两个功能.一个函数读取并分配每行的存储空间,第二个函数基本上调用第一个函数,分配指针并为每个分配的内存块分配地址,该存储块依次保存从文件读取的行到下一个指针.完成后,您将获得一个已分配的指针块,其中每个指针都保存着(指向)一个已分配块的地址(指向文件中的一行).

While you can put this all together in a single function, to help the learning process (and just for practical reusability), it is often easier to break this up into two function. One that reads and allocates storage for each line, and a second function that basically calls the first function, allocating pointers and assigning the address for each allocated block of memory holding a line read from the file to the next pointer in turn. When you are done, you have an allocated block of pointers where each of the pointers holds the address of (points to) an allocated block holding a line from the file.

您已表示要使用 fgetc()从文件中读取并一次读取一个字符.这样做没有任何问题,并且这种方法几乎没有损失,因为底层的I/O子系统提供了您实际上正在读取的读取缓冲区,而不是一次从磁盘读取一个字符.(大小因编译器而异,但通常通过 BUFSIZ 宏提供,Linux和Windows编译器均提供此大小)

You have indicated you want to read from the file with fgetc() and read a character at a time. There is nothing wrong with that, and there is little penalty to this approach since the underlying I/O subsystem provides a read-buffer that you are actually reading from rather than reading from disk one character at-a-time. (the size varies between compilers, but is generally provided through the BUFSIZ macro, both Linux and Windows compilers provide this)

实际上有无数种方法可以编写一个函数,该函数分配存储空间以容纳一行,然后一次从文件中读取一个字符,直到'\ n' EOF .您可以将指针返回到保存该行的已分配块,并传递一个指针参数以使用该行中包含的字符数进行更新,或者您可以让该函数返回行长并将指针的地址作为要在函数中分配和填充的参数.它是由你决定.一种方法是:

There are virtually an unlimited number of ways to write a function that allocates storage to hold a line and then reads a line from the file one character at-a-time until a '\n' or EOF is encountered. You can return a pointer to the allocated block holding the line and pass a pointer parameter to be updated with the number of characters contained in the line, or you can have the function return the line length and pass the address-of a pointer as a parameter to be allocated and filled within the function. It is up to you. One way would be:

#define NSHORT 5        /* no. of shortest lines to display */
#define LINSZ 128       /* initial allocation size for each line */
...

/** read line from 'fp' stored in allocated block assinged to '*s' and
 *  return length of string stored on success, on EOF with no characters
 *  read, or on failure, return -1. Block of memory sized to accommodate
 *  exact length of string with nul-terminating char. unless -1 returned,
 *  *s guaranteed to contain nul-terminated string (empty-string allowed).
 *  caller responsible for freeing allocated memory.
 */
ssize_t fgetcline (char **s, FILE *fp)
{
    int c;                              /* char read from fp */
    size_t n = 0, size = LINSZ;         /* no. of chars and allocation size */
    void *tmp = realloc (NULL, size);   /* tmp pointer for realloc use */
    
    if (!tmp)       /* validate every allocation/reallocation */
        return -1;
    
    *s = tmp;       /* assign reallocated block to pointer */
    
    while ((c = fgetc(fp)) != '\n' && c != EOF) {   /* read chars until \n or EOF */
        if (n + 1 == size) {                        /* check if realloc required */
            /* realloc using temporary pointer */
            if (!(tmp = realloc (*s, size + LINSZ))) {
                free (*s);              /* on failure, free partial line */
                return -1;              /* return -1 */
            }
            *s = tmp;                   /* assign reallocated block to pointer */
            size += LINSZ;              /* update allocated size */
        }
        (*s)[n++] = c;                  /* assign char to index, increment */
    }
    (*s)[n] = 0;                        /* nul-terminate string */
    
    if (n == 0 && c == EOF) {   /* if nothing read and EOF, free mem return -1 */
        free (*s);
        return -1;
    }
    
    if ((tmp = realloc (*s, n + 1)))    /* final realloc to exact length */
        *s = tmp;                       /* assign reallocated block to pointer */
    
    return (ssize_t)n;      /* return length (excluding nul-terminating char) */
}

(注意: ssize_t 是带符号的类型,提供了 size_t 的范围,该范围实际上允许返回 -1 .它在 sys/types.h 标头中提供.您可以根据需要调整类型)

(note: the ssize_t is a signed type providing the range of size_t that essentially allows the return of -1. it is provided in the sys/types.h header. you can adjust the type as desired)

fgetclines()函数对 realloc 进行最后一次调用,以将分配的大小缩小到容纳该行和终止符.

The fgetclines() function makes one final call to realloc to shrink the size of the allocation to the exact number of characters needed to hold the line and the nul-terminating character.

调用该函数以读取文件中的所有行,同时根据需要分配和重新分配指针,与上面的 fgetclines()函数对字符所做的基本上相同.它只是分配一些初始数量的指针,然后开始从文件中读取行,每次需要时都重新分配两倍的指针数量.它还添加了一个额外的指针来保存 NULL 作为标记,这将允许遍历所有指针,直到达到 NULL (这是可选的).将参数 n 更新为已存储的行数,以使其在调用函数中可用.该函数也可以用多种不同的方式编写,其中一种可能是:

The function called to read all lines in the file while allocation and reallocating pointers as required does essentially the same thing as the fgetclines() function above does for characters. It simply allocates some initial number of pointers and then begins reading lines from the file, reallocating twice the number of pointers each time it is needed. It also adds one additional pointer to hold NULL as a sentinel that will allow iterating over all pointers until NULL is reached (this is optional). The parameter n is updated to with the number of lines stored to make that available back in the calling function. This function too can be written in a number of different ways, one would be:

/** read each line from `fp` and store in allocated block returning pointer to
 *  allocateted block of pointers to each stored line with the final pointer
 *  after the last stored string set to NULL as a sentinel. 'n' is updated to
 *  the number of allocated and stored lines (excluding the sentinel NULL).
 *  returns valid pointer on success, NULL otherwise. caller is responsible for
 *  freeing both allocated lines and pointers.
 */
char **readfile (FILE *fp, size_t *n)
{
    size_t nptrs = LINSZ;               /* no. of allocated pointers */
    char **lines = malloc (nptrs * sizeof *lines);  /* allocated bock of pointers */
    void *tmp = NULL;                   /* temp pointer for realloc use */
    
    /* read each line from 'fp' into allocated block, assign to next pointer */
    while (fgetcline (&lines[*n], fp) != -1) {
        lines[++(*n)] = NULL;           /* set next pointer NULL as sentinel */
        if (*n + 1 >= nptrs) {          /* check if realloc required */
            /* allocate using temporary pointer to prevent memory leak on failure */
            if (!(tmp = realloc (lines, 2 * nptrs * sizeof *lines))) {
                perror ("realloc-lines");
                return lines;           /* return original poiner on failure */
            }
            lines = tmp;                /* assign reallocated block to pointer */
            nptrs *= 2;                 /* update no. of pointers allocated */
        }
    }
    
    /* final realloc sizing exact no. of pointers required */
    if (!(tmp = realloc (lines, (*n + 1) * sizeof *lines)))
        return lines;   /* return original block on failure */
    
    return tmp;         /* return updated block of pointers on success */
}

请注意,函数在文件中使用一个打开的 FILE * 参数,而不是在该函数中使用文件名打开.通常,您需要在调用函数中打开文件,并在调用函数以读取所有行之前验证文件是否已打开以供读取.如果无法在调用方中打开文件,则没有理由使函数全部从文件中开始读取行.

Note above, the function takes an open FILE* parameter for the file rather than taking a filename to open within the function. You generally want to open the file in the calling function and validate that it is open for reading before calling a function to read all the lines. If the file cannot be opened in the caller, there is no reason to make the function all to read the line from the file to begin with.

使用一种读取存储中文件中所有行的方法,接下来您需要转向按长度对行进行排序,以便输出5个最短的行(引号).由于您通常希望按顺序保留文件中的行,因此在保留原始顺序的同时按长度对行进行排序的最简单方法是制作指针的副本并按行长对指针的副本进行排序.例如,您的 lines 指针可以继续按原始顺序包含指针,而指针 sortedlines 的集合可以按行长对指针进行排序,例如

With a way to read an store all lines from your file done, you next need to turn to sorting the lines by length so you can output the 5 shortest lines (quotes). Since you will normally want to preserve the lines from your file in-order, the easiest way to sort the lines by length while preserving the original order is just to make a copy of the pointers and sort the copy of pointers by line length. For example, your lines pointer can continue to contain the pointers in original order, while the set of pointers sortedlines can hold the pointers in order sorted by line length, e.g.

int main (int argc, char **argv) {
    
    char **lines = NULL,            /* pointer to allocated block of pointers */
         **sortedlines = NULL;      /* copy of lines pointers to sort by length */

读取文件并填充 lines 指针后,您可以将指针复制到 sortedlines (包括前哨 NULL ),例如

After reading the file and filling the lines pointer, you can copy the pointers to sortedlines (including the sentinel NULL), e.g.

    /* alocate storage for copy of lines pointers (plus sentinel NULL) */
    if (!(sortedlines = malloc ((n + 1) * sizeof *sortedlines))) {
        perror ("malloc-sortedlines");
        return 1;
    }
    
    /* copy pointers from lines to sorted lines (plus sentinel NULL) */
    memcpy (sortedlines, lines, (n + 1) * sizeof *sortedlines);

然后,您只需调用 qsort 即可按长度对 sortedlines 中的指针进行排序.使用 qsort 的唯一工作是编写* compare`函数.比较函数的原型是:

Then you simply call qsort to sort the pointers in sortedlines by length. Your only job with qsort is to write the *compare` function. The prototype for the compare function is:

int compare (const void *a, const void *b);

a b 都是被排序的 pointers-to 元素.在您使用 char ** sortedlines; 的情况下,元素将是 pointer-to-char ,因此, a b 都将 pointer-to-pointer 的类型设置为 char .您只需编写一个比较函数,以便在 a 所指向的行的长度小于 b 的情况下返回小于零的.正确的顺序),如果长度相同则返回零(无需执行任何操作),如果 a 的长度大于 b <,则返回大于零/code>(需要交换).将比较写成两个条件的区别而不是简单的 a-b 可以防止所有潜在的溢出,例如

Both a and b will be pointers-to elements being sorted. In your case with char **sortedlines;, the elements will be pointer-to-char, so a and b will both have type pointer-to-pointer to char. You simply write a compare function so it will return less than zero if the length of line pointed to by a is less than b (already in the right order), return zero if the length is the same (no action needed) and return greater than zero if the length of a is greater than b (a swap is required). Writing the compare a the difference of two conditionals rather than simple a - b will prevent all potential overflow, e.g.

/** compare funciton for qsort, takes pointer-to-element in a & b */
int complength (const void *a, const void *b)
{
    /* a & b are pointer-to-pointer to char */
    char *pa = *(char * const *)a,      /* pa is pointer to string */
         *pb = *(char * const *)b;      /* pb is pointer to string */
    size_t  lena = strlen(pa),          /* length of pa */ 
            lenb = strlen(pb);          /* length of pb */
    
    /* for numeric types returing result of (a > b) - (a < b) instead
     * of result of a - b avoids potential overflow. returns -1, 0, 1.
     */
    return (lena > lenb) - (lena < lenb);
}

现在,您可以简单地将对象的集合,对象的数量,每个对象的大小以及用于对对象进行排序的函数传递给 qsort .排序的内容无关紧要-每次都以相同的方式工作.您完全没有理由需要写"字样.一种排序方式(出于教育目的除外)-即提供 qsort 的目的.例如,在这里使用 sortedlines ,您所需要做的就是:

Now you can simply pass the collection of objects, the number of object, the size of each object and the function to use to sort the objects to qsort. It doesn't matter what you need to sort -- it works the same way every time. There is no reason you should ever need to "go write" a sort (except for educational purposes) -- that is what qsort is provided for. For example, here with sortedlines, all you need is:

    qsort (sortedlines, n, sizeof *sortedlines, complength);    /* sort by length */

现在,您可以通过遍历 lines 来显示所有行,并通过 sortedlines 以升序显示所有行.显然要显示前5行,只需遍历 sortedlines 中的前5个有效指针.这同样适用于打开另一个文件以将这5行写入新文件.(留给您)

Now you can display all lines by iterating through lines and display all lines in ascending line length through sortedlines. Obviously to display the first 5 lines, just iterate over the first 5 valid pointers in sortedlines. The same applies to opening another file for writing and writing those 5 lines to a new file. (that is left to you)

就是这样.这有困难吗?-不.要做的事很琐碎-不.这是C语言编程的基本组成部分,需要学习和理解,但是与值得学习的东西没有什么不同.将所有部分放到一个工作程序中,以读取和显示文件中的所有行,然后对可以执行的最短的前5行进行排序和显示:

That's it. Is any of it difficult -- No. Is it trivial to do -- No. It is a basic part of programming in C that takes work to learn and to understand, but that is no different than anything worth learning. Putting all the pieces together in a working program to read and display all lines in a file and then sort and display the first 5 shortest lines you could do:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#define NSHORT 5        /* no. of shortest lines to display */
#define LINSZ 128       /* initial allocation size for each line */

/** compare funciton for qsort, takes pointer-to-element in a & b */
int complength (const void *a, const void *b)
{
    /* a & b are pointer-to-pointer to char */
    char *pa = *(char * const *)a,      /* pa is pointer to string */
         *pb = *(char * const *)b;      /* pb is pointer to string */
    size_t  lena = strlen(pa),          /* length of pa */ 
            lenb = strlen(pb);          /* length of pb */
    
    /* for numeric types returing result of (a > b) - (a < b) instead
     * of result of a - b avoids potential overflow. returns -1, 0, 1.
     */
    return (lena > lenb) - (lena < lenb);
}

/** read line from 'fp' stored in allocated block assinged to '*s' and
 *  return length of string stored on success, on EOF with no characters
 *  read, or on failure, return -1. Block of memory sized to accommodate
 *  exact length of string with nul-terminating char. unless -1 returned,
 *  *s guaranteed to contain nul-terminated string (empty-string allowed).
 *  caller responsible for freeing allocated memory.
 */
ssize_t fgetcline (char **s, FILE *fp)
{
    int c;                              /* char read from fp */
    size_t n = 0, size = LINSZ;         /* no. of chars and allocation size */
    void *tmp = realloc (NULL, size);   /* tmp pointer for realloc use */
    
    if (!tmp)       /* validate every allocation/reallocation */
        return -1;
    
    *s = tmp;       /* assign reallocated block to pointer */
    
    while ((c = fgetc(fp)) != '\n' && c != EOF) {   /* read chars until \n or EOF */
        if (n + 1 == size) {                        /* check if realloc required */
            /* realloc using temporary pointer */
            if (!(tmp = realloc (*s, size + LINSZ))) {
                free (*s);              /* on failure, free partial line */
                return -1;              /* return -1 */
            }
            *s = tmp;                   /* assign reallocated block to pointer */
            size += LINSZ;              /* update allocated size */
        }
        (*s)[n++] = c;                  /* assign char to index, increment */
    }
    (*s)[n] = 0;                        /* nul-terminate string */
    
    if (n == 0 && c == EOF) {   /* if nothing read and EOF, free mem return -1 */
        free (*s);
        return -1;
    }
    
    if ((tmp = realloc (*s, n + 1)))    /* final realloc to exact length */
        *s = tmp;                       /* assign reallocated block to pointer */
    
    return (ssize_t)n;      /* return length (excluding nul-terminating char) */
}

/** read each line from `fp` and store in allocated block returning pointer to
 *  allocateted block of pointers to each stored line with the final pointer
 *  after the last stored string set to NULL as a sentinel. 'n' is updated to
 *  the number of allocated and stored lines (excluding the sentinel NULL).
 *  returns valid pointer on success, NULL otherwise. caller is responsible for
 *  freeing both allocated lines and pointers.
 */
char **readfile (FILE *fp, size_t *n)
{
    size_t nptrs = LINSZ;               /* no. of allocated pointers */
    char **lines = malloc (nptrs * sizeof *lines);  /* allocated bock of pointers */
    void *tmp = NULL;                   /* temp pointer for realloc use */
    
    /* read each line from 'fp' into allocated block, assign to next pointer */
    while (fgetcline (&lines[*n], fp) != -1) {
        lines[++(*n)] = NULL;           /* set next pointer NULL as sentinel */
        if (*n + 1 >= nptrs) {          /* check if realloc required */
            /* allocate using temporary pointer to prevent memory leak on failure */
            if (!(tmp = realloc (lines, 2 * nptrs * sizeof *lines))) {
                perror ("realloc-lines");
                return lines;           /* return original poiner on failure */
            }
            lines = tmp;                /* assign reallocated block to pointer */
            nptrs *= 2;                 /* update no. of pointers allocated */
        }
    }
    
    /* final realloc sizing exact no. of pointers required */
    if (!(tmp = realloc (lines, (*n + 1) * sizeof *lines)))
        return lines;   /* return original block on failure */
    
    return tmp;         /* return updated block of pointers on success */
}

/** free all allocated memory (both lines and pointers) */
void freelines (char **lines, size_t nlines)
{
    for (size_t i = 0; i < nlines; i++) /* loop over each pointer */
        free (lines[i]);                /* free allocated line */
    
    free (lines);       /* free pointers */
}

int main (int argc, char **argv) {
    
    char **lines = NULL,            /* pointer to allocated block of pointers */
         **sortedlines = NULL;      /* copy of lines pointers to sort by length */
    size_t n = 0;                   /* no. of pointers with allocated lines */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }
    
    if (!(lines = readfile (fp, &n)))   /* read all lines in file, fill lines */
        return 1;
    
    if (fp != stdin)                /* close file if not stdin */
        fclose (fp);
    
    /* alocate storage for copy of lines pointers (plus sentinel NULL) */
    if (!(sortedlines = malloc ((n + 1) * sizeof *sortedlines))) {
        perror ("malloc-sortedlines");
        return 1;
    }
    
    /* copy pointers from lines to sorted lines (plus sentinel NULL) */
    memcpy (sortedlines, lines, (n + 1) * sizeof *sortedlines);
    
    qsort (sortedlines, n, sizeof *sortedlines, complength);    /* sort by length */
    
    /* output all lines from file (first screen) */
    puts ("All lines:\n\nline : text");
    for (size_t i = 0; i < n; i++)
        printf ("%4zu : %s\n", i + 1, lines[i]);
    
    /* output first five shortest lines (second screen) */
    puts ("\n5 shortest lines:\n\nline : text");
    for (size_t i = 0; i < (n >= NSHORT ? NSHORT : n); i++)
        printf ("%4zu : %s\n", i + 1, sortedlines[i]);
    
    freelines (lines, n);       /* free all allocated memory for lines */
    free (sortedlines);         /* free block of pointers */
}

(注意:,文件将从作为第一个参数传递给程序的文件名中读取,或者如果没有给出参数,则从 stdin 中读取)

(note: the file reads from the filename passed as the first argument to the program, or from stdin if no argument is given)

示例输入文件

$ cat dat/fleascatsdogs.txt
My dog
My fat cat
My snake
My dog has fleas
My cat has none
Lucky cat
My snake has scales

使用/输出示例

$ ./bin/fgetclinesimple dat/fleascatsdogs.txt
All lines:

line : text
   1 : My dog
   2 : My fat cat
   3 : My snake
   4 : My dog has fleas
   5 : My cat has none
   6 : Lucky cat
   7 : My snake has scales

5 shortest lines:

line : text
   1 : My dog
   2 : My snake
   3 : Lucky cat
   4 : My fat cat
   5 : My cat has none

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)不再需要它时可以释放.

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

当务之急是使用一个内存错误检查程序来确保您不尝试访问内存或不在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存.

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

对于Linux, valgrind 是通常的选择.每个平台都有类似的内存检查器.它们都很容易使用,只需通过它运行程序即可.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/fgetclinesimple dat/fleascatsdogs.txt
==5900== Memcheck, a memory error detector
==5900== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5900== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5900== Command: ./bin/fgetclinesimple dat/fleascatsdogs.txt
==5900==
All lines:

line : text
   1 : My dog
   2 : My fat cat
   3 : My snake
   4 : My dog has fleas
   5 : My cat has none
   6 : Lucky cat
   7 : My snake has scales

5 shortest lines:

line : text
   1 : My dog
   2 : My snake
   3 : Lucky cat
   4 : My fat cat
   5 : My cat has none
==5900==
==5900== HEAP SUMMARY:
==5900==     in use at exit: 0 bytes in 0 blocks
==5900==   total heap usage: 21 allocs, 21 frees, 7,938 bytes allocated
==5900==
==5900== All heap blocks were freed -- no leaks are possible
==5900==
==5900== For counts of detected and suppressed errors, rerun with: -v
==5900== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误.

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

这里有很多东西,就像任何"X怎么做?"一样,问题是,魔鬼总是很详细,每个功能的正确使用,每个输入或分配/重新分配的正确验证.每个部分与另一个部分一样重要,以确保您的代码以定义的方式完成所需的工作.仔细检查一下,花些时间来整理各个部分,如果还有其他问题,请告诉我.

There is a lot here, and as with any "how do it do X?" question, the devil is always in the detail, the proper use of each function, the proper validation of each input or allocation/reallocation. Each part is just as important as the other to ensure your code does what you need it to do -- in a defined way. Look things over, take your time to digest the parts, and let me know if you have further questions.

这篇关于需要帮助以C中的逐个字符读取文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆