C-从文件末尾读取意外的随机字符

问题描述 投票:0回答:2

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

这是文件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);
    }

也许无法完全省略字符,但在拆分过程中可能会忽略它们?

谢谢杰克。

c file eof unexpectendoffile
2个回答
1
投票

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

fp = fopen(WORDS_FILENAME, "r");

根据C标准7.21.9.4 The ftell function, paragraph 2

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

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

因此,您必须以binary模式打开文件才能使用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()的注释

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

7.21.9.2 The fseek function, paragraph 3

对于二进制流,通过从wherece指定的位置加上偏移量,获得从文件开头开始以字符为单位的新位置。如果位为SEEK_SET,则指定位置为文件的开头;如果为SEEK_CUR,则为文件位置指示符的当前值;如果为SEEK_END,则为文件结尾。二进制流不必有意义地支持具有SEEK_END值的fseek调用。

[Footnote 268偶数状态:

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

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


0
投票

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.