使用fgetc()逐行读取c文件

hel*_*hod 6 c file-io

我就是这样做的,但我不确定这是首选的习语:

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);
}
Run Code Online (Sandbox Code Playgroud)

这没关系,有什么建议可以改善吗?

Jon*_*ler 13

如果你不打算使用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);
}
Run Code Online (Sandbox Code Playgroud)

它只识别换行符作为行尾; 它放弃了换行符.它不会溢出缓冲区; 它不会丢弃多余的字符,所以如果要求读取一个很长的行,它将以块的形式读取该行; 它返回读取的字符数.如果你需要区分溢出和恰好是缓冲区长度的行 - 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);
}
Run Code Online (Sandbox Code Playgroud)

这有无穷无尽的小变种,例如,如果必须截断该行,则丢弃任何多余的字符.如果你想处理DOS,(旧)Mac或Unix行结尾,那么从Kernighan&Pike的"编程实践"中借用CSV代码(一本优秀的书)并使用:

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);
}
Run Code Online (Sandbox Code Playgroud)

然后你可以用它代替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);
}
Run Code Online (Sandbox Code Playgroud)

处理整个过程的另一种方法是使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在上下文中,您打开文件并检查其有效性,然后调用:

copy_file(fp, stdout);
Run Code Online (Sandbox Code Playgroud)