【问题标题】:Simple C program to read a file line by line逐行读取文件的简单C程序
【发布时间】:2017-12-13 16:25:29
【问题描述】:

我想做的是读取文件的整个第一行,但在第一行之后只读取以下行,直到遇到空格。我的最终目标是通过添加/减去该行的时间来询问用户他们想要编辑哪一行。

示例文件

My test file
00:19.1 123456
00:35.4 testing whitespace end

期望的输出

1: My test file
2: 00:19.1
3: 00:35.4

代码:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE *fptr1, *fptr2;

    char filechar[40];
    char c[50];
    int line_number = 1;
    int replace_line, temp = 1;

    printf("Please enter a file name: ");
    scanf("%s", &filechar);

    if ((fptr1 = fopen(filechar, "r")) == NULL)
    {
        printf("Error locating desired file");
        exit(1);
    }

    c = getc(fptr1);
    while (c != EOF)
    {
        //printf("%d: %c",line_number, c);
        printf("%s",c);
        c = getc(fptr1);
        //line_number++;
    }
    return 0;
}

【问题讨论】:

  • 你的编译器是否有getline函数:en.cppreference.com/w/c/experimental/dynamic/getline
  • scanf("%s", &amp;filechar); 中删除&amp;scanf() 需要一个char *,而您传递的是一个不同类型的char (*)[50](尽管该值恰好与只是filechar 这样你就可以侥幸逃脱了,有点)。
  • 使用fgets() 或POSIX getline() 读取行。然后使用strchr() 或类似方法查找第一个空白。或者使用这两个中的一个读取第一行,然后使用scanf("%s %*[^\n]%*c", first_field); 读取后续的第一个字段——请参阅scanf() 以获取对此的解释。总的来说,我会选择基于行的输入和strchr();你可以使用strtok()sscanf()
  • 如果您知道第一行的最大长度,请使用fgets。否则,看看gist.github.com/mrkline/99630570e839a4af0e3b

标签: c codeblocks


【解决方案1】:

在 C 中,您有面向字符的输入 函数(例如getcharfgetc),您有格式化输入 函数(例如scanf 系列)和那么你就有了面向行的输入功能。 (例如fgets 和 POSIX getline)。当您阅读 数据时,面向行的输入 函数是完成这项工作的合适工具。 (使用scanf 获取用户输入有许多新的(甚至不是那么新的)C 程序员会陷入的陷阱)

所有面向行的函数读取并包含'\n' 在它们填充的缓冲区中。如果稍后将在您的代码中使用它,您可以并且应该从结果缓冲区中删除换行符。一个简单的

size_t n = strlen (buf);
if (buf[n-1] == '\n') 
    buf[--n] = 0;

就是用 nul-terminating 字符覆盖尾随 '\n' 所需的全部内容。如果您只是立即打印该行而不是将其存储以供以后使用,那么不值得删除换行符(只需在输出 format string 中考虑它)。

将这些部分放在一起,您可以读取每一行,通过简单地输出它来处理第一行,然后对于剩余的每一行,从读取的完整字符串中解析时间(可能是经过的时间fgetssscanf 并按照您的指定格式化输出。例如

#include <stdio.h>

#define MAXC 64     /* define constants, don't use magic number in code */

int main (int argc, char **argv) {

    char buf[MAXC] = "";    /* buffer to hold each line -- size as reqd */
    int line = 1;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    while (fgets (buf, sizeof buf, fp)) {   /* read each line in file  */
        char et[MAXC] = "";                 /* buffer for holding time */
        if (line == 1)                      /* if 1st line, just print */
            printf ("%d : %s", line, buf);  /* note: \n included by fgets */
        else {
            if (sscanf (buf, "%s", et) != 1) { /* parse up to first whitespace */
                fprintf (stderr, "error: invalid conversion, line %d\n", line);
                return 1;
            }
            printf ("%d : %s\n", line, et); /* output elapsed time only  */
        }
        line++;                             /* increment line count */
    }

    if (fp != stdin) fclose (fp);           /* close file if not stdin */

    return 0;
}

注意: 您应该通过在sscanf 格式字符串中包含一个field-width 说明符(例如sscanf (buf, "%63s", et),即您所能做的就是在代码中包含 magic numbers,因为无法直接为 sscanf 指定可变宽度说明符——除非您创造性地使用 sprintf 创建 格式化字符串提前-但那是另一天..

输入文件示例

$ cat dat/et.txt
My test file
00:19.1 123456
00:35.4 testing whitespace end

使用/输出示例

$ ./bin/et <dat/et.txt
1 : My test file
2 : 00:19.1
3 : 00:35.4

查看一下,如果您有任何其他问题,请告诉我。

(注意:我将文件名作为程序的第一个参数,或者如果没有给出文件名,则从stdin 读取。C 提供了命令行参数——使用它们。如果需要,可以提示输入,否则,仅在命令行上指定参数会容易得多:)

【讨论】:

  • 谢谢你这在一定程度上有效,你能解释一下你添加的无效转换行吗?我的测试文件中有四行我出错了。 Line 1: My test file Line 2: 00:19.1 009-021 Line 3: 00:35.4 017-003 018-003 Line 4: 00:35.5 017-000 018-000
  • 当然,这只是一个验证 %s 的转换是由sscanf 执行的。 (本质上我只是检查sscanfreturn)如果由于某种原因sscanf 失败(使用%s 不太可能),您不想继续使用et 进行处理将是未定义的行为。 C 不提供任何安全网,因此由您来验证所有输入、转换等...:) 回忆,所有scanf 函数return 执行成功转换的次数。
  • 好的,我现在明白了。谢谢大家的帮助!
  • 很高兴我能帮上忙。 C 是一种非常灵活的语言,可让您在底层完全控制机器。俗话说(“权力越大,责任越大……”)没有什么比 C 更真实的了。验证、验证、验证……:)
  • 有没有办法覆盖我在 while 循环中读取的行,同时保留空白后的字符不变?
【解决方案2】:

如果此 C 代码可以帮助您,请尝试。它只是逐行读取文件并用字符串终止字符\0 替换空格。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

char* replace_char(char* str, char find, char replace){
    char *current_pos = strchr(str,find);
    while (current_pos){
        *current_pos = replace;
        current_pos = strchr(current_pos,find);
    }
    return str;
}
int main(void)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    fp = fopen("/home/developer/CLionProjects/untitled4/download.out", "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);
    int count=0;
    while ((read = getline(&line, &len, fp)) != -1) {
        if (count==0) printf("%s", line);
        else printf("%s\n", replace_char(line, ' ', '\0'));
        count++;
    }

    fclose(fp);
    if (line)
        free(line);
    exit(EXIT_SUCCESS);
}

文件

My test file
00:19.1 123456
00:35.4 testing whitespace end

输出

My test file
00:19.1
00:35.4

【讨论】:

  • 我在使用这种方法时遇到了两个错误。第一个是对 getline 的未定义引用,第二个是 ld 返回 1 个退出状态。
猜你喜欢
  • 2014-06-21
  • 2012-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-11
  • 1970-01-01
相关资源
最近更新 更多