0

我正在尝试从 csv 文件中读取逗号分隔的单词列表,并且在处理由 C 读取时出现在文件末尾的接缝随机字符时遇到问题。末尾的字符当我从列表中添加/删除单词时,文件似乎完全改变了。

这是文件中包含的内容:johnny,david,alan,rodney,bob,ronald,andrew,hola,goodbye. 那是完全复制的,最后没有意外的空格或回车。

以下是程序读取的内容:

这是在文本中读取的代码:

    char* name;
    FILE *fp;
    char *fcontent;
    int wordCount = 0;
    char delim = ',';
    long fsize;
    bool end = false;
    char guessedLetters[26];
    int guessNum = 0;
    int lives = 0;

    for (int i = 0; i < 26; i++) {
        guessedLetters[i] = '\0';
    }

    fp = fopen(WORDS_FILENAME, "r");

    if (fp == NULL) {
        printf("Words File Exception: Exiting.");
        return 1;
    }

    fseek(fp, 0L, SEEK_END);
    fsize = ftell(fp);
    fseek(fp, 0L, SEEK_SET);

    fcontent = (char*)calloc(fsize, sizeof(char));

    if (fcontent == NULL) {
        printf("No words in file: Exiting.");
        return 1;
    }

    fread(fcontent, sizeof(char), fsize, fp);
    char *fcontent2 = malloc(strlen(fcontent + 1));
    strcpy(fcontent2, fcontent);
    fclose(fp);

单词被拆分成一个单词数组,流氓字符一直附加到最后一个单词的末尾,这在程序的后期造成了很多问题。

这是将字符串拆分为数组的代码wordArr

char wordArr[wordCount][15];

    char *ptr2 = strtok(fcontent2, &delim);
    int count = 0;

    while (ptr2 != NULL) {
        strcpy(wordArr[count], ptr2);
        count++;
        ptr2 = strtok(NULL, &delim);
    }

也许如果不可能完全忽略读取的字符,那么可以在拆分过程中忽略它们?

谢谢,杰克。

4

2 回答 2

3

首先,以文本模式打开文件:

fp = fopen(WORDS_FILENAME, "r");

根据 C 标准7.21.9.4 ftell 函数,第 2 段

ftell 函数获取 stream 指向的流的文件位置指示符的当前值。对于二进制流,该值是从文件开头开始的字符数。对于文本流,其文件位置指示符包含未指定的信息,fseek 函数可使用该信息将流的文件位置指示符返回到 ftell 调用时的位置;两个这样的返回值之间的差异不一定是衡量写入或读取字符数的有意义的量度。

您不能ftell()在文本流上使用来判断可以读取多少字节。

因此,您必须以二进制模式打开文件才能使用ftell()(但请参阅下面的注释):

fp = fopen(WORDS_FILENAME, "rb");

现在你有了文件大小:

fseek(fp, 0L, SEEK_END);
fsize = ftell(fp);
fseek(fp, 0L, SEEK_SET);

fcontent = (char*)calloc(fsize, sizeof(char));

但是,这没有给任何终结者留下空间'\0',所以应该是

// no need to cast a void * in C, and sizeof(char)
// is **always** one by definition
fcontent = calloc(fsize + 1 , 1);

现在,您将获得文件内容的终止字符串。

fseek()关于二进制流的注意事项

根据fseek()C 标准,用于到达二进制流的末尾实际上是未定义的行为。

根据7.21.9.2 fseek 功能,第 3 段

对于二进制流,新位置(以文件开头的字符为单位)是通过将偏移量添加到由 wherece 指定的位置来获得的。如果 wherece 是 SEEK_SET,则指定位置是文件的开头,如果是 SEEK_CUR,则为文件位置指示符的当前值,如果是 SEEK_END,则为文件结尾。二进制流不需要有意义地支持 wherece 值为 SEEK_END 的 fseek 调用。

脚注 268甚至指出:

将文件位置指示器设置为文件结尾,与 fseek(file, 0, SEEK_END) 一样,对于二进制流(因为可能的尾随空字符)或任何具有状态相关编码的流具有未定义的行为确保以初始换档状态结束。

您可以使用的唯一原因fseek(fp, 0L, SEEK_END);是因为大多数操作系统都扩展了 C 语言并实际定义了它可以工作。

于 2020-01-12T00:36:47.280 回答
2

读取的数据不包含终止空字符。

您需要检查读取字符的计数,然后“手动”设置终止空字符:

int cnt = fread(fcontent, sizeof(char), fsize, fp);
fcontent[cnt] = '\0';

当然,好的做法是cnt在将其用作数组索引之前检查它不是负数(读取错误)。

于 2020-01-12T00:22:34.643 回答