【问题标题】:extract comma separated strings from txt file in C从C中的txt文件中提取逗号分隔的字符串
【发布时间】:2020-05-30 15:51:40
【问题描述】:

我需要从文件中读取以逗号分隔的不同字符串并将它们存储到数组中。

我有以下代码,是我在线阅读不同问题时开发的。

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

int main (){
int N = 300; 
int L = 1000;
char Nseq[N][L];

FILE *myfile;
char *token;
const char s[2] = ",";
char line[300];
char* filename = "pathtofile.txt";
int n = 0;

myfile = fopen(filename, "r");
if (myfile == NULL) {printf("could not open file %s", filename); exit(0);}
while (fgets(line, sizeof(line), myfile) != NULL){
  token = strtok(line, s);
  while (token != NULL){
    strcpy(Nseq[n], token);
    printf("%s\t%u\n", token, n);
    token = strtok(NULL, s);
    n++;
  }
}
fclose(myfile);
}

我的txt文件如下:

1AAAAAAAAAAAAAAAAAAAAAAAAAAAA,2AAAAAAAAAAAAAAAAAAAAAAAAAAAA,3AAAAAAAAAAAAAAAAAAAAAAAAAAAA,4AAAAAAAAAAAAAAAAAAAAAAAAAAAA,5AAAAAAAAAAAAAAAAAAAAAAAAAAAA,6AAAAAAAAAAAAAAAAAAAAAAAAAAAA,7AAAAAAAAAAAAAAAAAAAAAAAAAAAA,8AAAAAAAAAAAAAAAAAAAAAAAAAAAA,9AAAAAAAAAAAAAAAAAAAAAAAAAAAA,10AAAAAAAAAAAAAAAAAAAAAAAAAAAA,11AAAAAAAAAAAAAAAAAAAAAAAAAAAA,12AAAAAAAAAAAAAAAAAAAAAAAAAAAA,13AAAAAAAAAAAAAAAAAAAAAAAAAAAA,14AAAAAAAAAAAAAAAAAAAAAAAAAAAA,15AAAAAAAAAAAAAAAAAAAAAAAAAAAA,16AAAAAAAAAAAAAAAAAAAAAAAAAAAA,17AAAAAAAAAAAAAAAAAAAAAAAAAAAA,18AAAAAAAAAAAAAAAAAAAAAAAAAAAA,19AAAAAAAAAAAAAAAAAAAAAAAAAAAA,20AAAAAAAAAAAAAAAAAAAAAAAAAAAA,21AAAAAAAAAAAAAAAAAAAAAAAAAAAA,22AAAAAAAAAAAAAAAAAAAAAAAAAAAA,23AAAAAAAAAAAAAAAAAAAAAAAAAAAA,24AAAAAAAAAAAAAAAAAAAAAAAAAAAA,25AAAAAAAAAAAAAAAAAAAAAAAAAAAA,26AAAAAAAAAAAAAAAAAAAAAAAAAAAA,27AAAAAAAAAAAAAAAAAAAAAAAAAAAA,28AAAAAAAAAAAAAAAAAAAAAAAAAAAA,29AAAAAAAAAAAAAAAAAAAAAAAAAAAA,30AAAAAAAAAAAAAAAAAAAAAAAAAAAA,

有 30 个字符串,没有换行符。

我的问题是,当我运行代码时,我得到以下输出:

1AAAAAAAAAAAAAAAAAAAAAAAAAAAA   0
2AAAAAAAAAAAAAAAAAAAAAAAAAAAA   1
3AAAAAAAAAAAAAAAAAAAAAAAAAAAA   2
4AAAAAAAAAAAAAAAAAAAAAAAAAAAA   3
5AAAAAAAAAAAAAAAAAAAAAAAAAAAA   4
6AAAAAAAAAAAAAAAAAAAAAAAAAAAA   5
7AAAAAAAAAAAAAAAAAAAAAAAAAAAA   6
8AAAAAAAAAAAAAAAAAAAAAAAAAAAA   7
9AAAAAAAAAAAAAAAAAAAAAAAAAAAA   8
10AAAAAAAAAAAAAAAAAAAAAAAAAAA   9
A       10
11AAAAAAAAAAAAAAAAAAAAAAAAAAAA  11
12AAAAAAAAAAAAAAAAAAAAAAAAAAAA  12
13AAAAAAAAAAAAAAAAAAAAAAAAAAAA  13
14AAAAAAAAAAAAAAAAAAAAAAAAAAAA  14
15AAAAAAAAAAAAAAAAAAAAAAAAAAAA  15
16AAAAAAAAAAAAAAAAAAAAAAAAAAAA  16
17AAAAAAAAAAAAAAAAAAAAAAAAAAAA  17
18AAAAAAAAAAAAAAAAAAAAAAAAAAAA  18
19AAAAAAAAAAAAAAAAAAAAAAAAAAAA  19
20AAAAAAAAAAAAAAAA      20
AAAAAAAAAAAA    21
21AAAAAAAAAAAAAAAAAAAAAAAAAAAA  22
22AAAAAAAAAAAAAAAAAAAAAAAAAAAA  23
23AAAAAAAAAAAAAAAAAAAAAAAAAAAA  24
24AAAAAAAAAAAAAAAAAAAAAAAAAAAA  25
25AAAAAAAAAAAAAAAAAAAAAAAAAAAA  26
26AAAAAAAAAAAAAAAAAAAAAAAAAAAA  27
27AAAAAAAAAAAAAAAAAAAAAAAAAAAA  28
28AAAAAAAAAAAAAAAAAAAAAAAAAAAA  29
29AAAAAAAAAAAAAAAAAAAAAAAAAAAA  30
30AAAAA 31
AAAAAAAAAAAAAAAAAAAAAAA 32

        33

我尝试过不同的长度,但迟早我会得到这些奇怪的分裂。

有人知道为什么会这样吗?谢谢!

【问题讨论】:

    标签: c fgets strtok


    【解决方案1】:

    您的文本文件大小为 921 个字符,并且是 单行 行。

    您的line 缓冲区只有 300 个字符。

    所以,你被截断了。

    另外,请注意您的文件有 no 换行符。而且,您的代码没有处理 换行符的情况(特别是,如果该行以 ,&lt;newline&gt; 结尾)。

    简单的解决方案是增加line的大小,使其大于文件大小(例如)char line[10000];

    长期解决方案是使用(例如)fgetc 逐字符读取文件并复制到 Nseq[n] 并在分隔符后存储/打印令牌。

    或者,您可以stat 文件,并使用malloc 分配文件大小的缓冲区。

    但是,虽然稍微高级一些,但最快的方法[特别是对于大文件],是 stat 文件,mmap 它,然后扫描缓冲区。这在任何 64 位机器上都可以正常工作,或者您可以在 32 位机器上将其映射为块


    这是一个使用fgetc的版本:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int
    main(void)
    {
        int N = 300;
        int L = 1000;
        char Nseq[N][L];
    
        FILE *myfile;
        char *token;
        char *filename = "pathtofile.txt";
        int chr;
        int n = 0;
    
        myfile = fopen(filename, "r");
        if (myfile == NULL) {
            printf("could not open file %s", filename);
            exit(0);
        }
    
        token = Nseq[n];
    
        while (1) {
            chr = fgetc(myfile);
            if (chr == EOF)
                break;
    
            switch (chr) {
            case ',':
            case '\n':
                *token = 0;
                if (token > Nseq[n]) {
                    printf("%s\t%u\n", Nseq[n], n);
                    ++n;
                }
                token = Nseq[n];
                break;
    
            default:
                *token++ = chr;
                break;
            }
        }
    
        fclose(myfile);
    
        return 0;
    }
    

    这是一个使用malloc的版本:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/stat.h>
    
    int
    main(void)
    {
        int N = 300;
        int L = 1000;
        char Nseq[N][L];
    
        FILE *myfile;
        char *token;
        const char s[2] = ",";
        char *line;
        int len;
        char *filename = "pathtofile.txt";
        int err;
        struct stat st;
        int n = 0;
    
        err = stat(filename,&st);
        if (err < 0) {
            printf("could not stat file %s", filename);
            exit(1);
        }
        len = st.st_size + 1;
    
        line = malloc(len);
    
        myfile = fopen(filename, "r");
        if (myfile == NULL) {
            printf("could not open file %s", filename);
            exit(1);
        }
    
        while (fgets(line, len, myfile) != NULL) {
            token = strtok(line, s);
            while (token != NULL) {
                strcpy(Nseq[n], token);
                printf("%s\t%u\n", token, n);
                token = strtok(NULL, s);
                n++;
            }
        }
    
        fclose(myfile);
    
        return 0;
    }
    

    这是一个使用mmap的版本:

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    
    int
    main(void)
    {
        int N = 300;
        int L = 1000;
        char Nseq[N][L];
    
        char *token;
        char *line;
        char *cur;
        char *end;
        char *filename = "pathtofile.txt";
        int fd;
        int chr;
        int n = 0;
        int err;
        struct stat st;
        size_t len;
    
        fd = open(filename,O_RDONLY);
        if (fd < 0) {
            printf("could not open file %s", filename);
            exit(1);
        }
    
        err = fstat(fd,&st);
        if (err < 0) {
            printf("could not stat file %s", filename);
            exit(1);
        }
        len = st.st_size;
    
        line = mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
        if (line == MAP_FAILED) {
            printf("could not mmap file %s", filename);
            exit(1);
        }
    
        cur = line;
        end = &line[len];
        token = Nseq[n];
    
        for (cur = line;  cur < end;  ++cur) {
            chr = *cur;
    
            switch (chr) {
            case ',':
            case '\n':
                *token = 0;
                if (token > Nseq[n]) {
                    printf("%s\t%u\n", Nseq[n], n);
                    ++n;
                }
                token = Nseq[n];
                break;
    
            default:
                *token++ = chr;
                break;
            }
        }
    
        munmap(line,len);
        close(fd);
    
        return 0;
    }
    

    【讨论】:

    • 关于:*token++ = chr; 一般情况下,需要注意 C 运算符的优先级。建议:*(token++) = chr;
    • 关于这种说法:printf("could not open file %s", filename); 错误信息应该输出到stderr,而不是stdout。当错误来自 C 库函数时,还应输出系统认为发生错误的文本原因。建议:fprintf( stderr, "could not open file %s due to %s\n", filename, strerror( errno ));需要头文件:errno.hstring.h
    • @user3629249 关于printf。是的,我当然知道这样做,但是已经有足够的代码更改了。而且,关于*token++ 的事情,我很抱歉,但我不得不不同意。括号不是必要的,它们只是使代码混乱,没有它们是相当惯用的,并且在我编写和阅读的数百万行C代码中,我不记得在生产代码中看到过*(token++)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-24
    • 1970-01-01
    相关资源
    最近更新 更多