我正在编写一个程序,该程序可以读取一个近200万行的文件。该文件采用格式为整数 ID 选项卡,带有艺术家姓名字符串。
6821361 Selinsgrove High School Chorus
10151460 greek-Antique
10236365 jnr walker & the all-stars
6878792 Grieg - Kraggerud, Kjekshus
6880556 Mr. Oiseau
6906305 stars on 54 (maxi single)
10584525 Jonie Mitchel
10299729 エリス レジーナ/アントニオ カルロス ジョビン
上面是文件中某些项目的示例(不是某些行不遵循特定格式(。我的程序工作文件,直到它到达示例的最后一行,然后它无休止地打印エリス レジーナ/アントニオ カルロス ジョビ343203
.
struct artist *read_artists(char *fname)
{
FILE *file;
struct artist *temp = (struct artist*)malloc(sizeof(struct artist));
struct artist *head = (struct artist*)malloc(sizeof(struct artist));
file = fopen("/Users/Daniel/Library/Developer/Xcode/DerivedData/project_Audioscrobbler_Artists-hgwyqpinuoxayzbmvarcjxryqnrz/Build/Products/Debug/artist_data.txt", "r");
if(file == 0)
{
perror("fopen");
exit(1);
}
int artist_ID;
char artist_name[650];
while(!feof(file))
{
fscanf(file, "%dt%65[^tn]n", &artist_ID, artist_name);
temp = create_play(artist_ID, artist_name, 0, -1);
head = add_play(head, temp);
printf("%sn", artist_name);
}
fclose(file);
//print_plays(head);
return head;
}
以上是我从文件中读取的代码。你能帮忙解释一下出了什么问题吗?
正如注释所示,一个问题是 while(!feof(file(( 链接的内容将详细解释为什么这不是一个好主意,但总而言之,引用链接中的一个答案:
(!feof(file((...
。是错误的,因为它测试的是 无关紧要,无法测试您需要知道的内容。这 结果是你错误地执行了假定它 正在访问已成功读取的数据,而实际上这从未 发生。- 凯瑞克·
在您的情况下,这种用法不会导致您的问题,但正如 Kerrek 解释的那样,可能会发生,掩盖它。
您可以将其替换为 fgets(...)
:
char lineBuf[1000];//make length longer or shorter for your purpose
file = fopen("/Users/Daniel/Library/Developer/Xcode/DerivedData/project_Audioscrobbler_Artists-hgwyqpinuoxayzbmvarcjxryqnrz/Build/Products/Debug/artist_data.txt", "r");
if(!file) return -1;
while(fgets (lineBuf, sizeof(lineBuf), file))
{
//process each line here
//But processing Japanese characters
//will require special considerations.
//Refer to the link below for UNICODE tips
}
Unicode in C 和 C++...
特别是,您将需要使用足以包含要处理的不同大小字符的变量类型。 该链接对此进行了非常详细的讨论。
以下为摘录:
"char" no longer means character I hereby recommend referring to character codes in C programs using a 32-bit unsigned integer type. Many platforms provide a
"wchar_t"(宽字符(类型,但不幸的是要避免 因为有些编译器只分配了 16 位——不足以表示 统一码。无论你需要在哪里传递一个单独的角色, 将"char"更改为"无符号int"或类似内容。唯一剩余的用途 "char"类型表示"字节"。
编辑:
在上面的评论中,您声明但它失败的字符串长度为 66 字节。 由于您正在读取"char"数组,因此完成字符所需的字节在包含最后一个必要的字节之前被截断了一个字节。 ASCII 字符可以包含在单个char
空间中。 日语字符不能。 如果您使用的是unsigned int
数组而不是char
数组,则最后一个字节将被包括在内。
OP 的代码失败,因为未检查fscanf()
的结果。
fscanf(file, "%dt%65[^tn]n", &artist_ID, artist_name);
fscanf()
在 65 char
"エリス レジーナ/アントニオ カルロス ジョビン"
中阅读。 然而,这个字符串以 UTF8 编码,长度为 66。 最后一个'ン'
是代码 227、131、179(八进制 343 203 263(,只读取了最后 2 个。 打印artist_name
时,将显示以下内容。
エリス レジーナ/アントニオ カルロス ジョビ343203
现在开始问题了。 最后的char 179
留在file
. 在下一个fscanf()
,它失败,因为char 179
没有转换为int
("%d"
(。 所以fscanf()
返回 0。 由于代码没有检查fscanf()
的结果,它没有意识到artist_ID
和artist_name
是以前遗留下来的,因此打印相同的文本。
由于feof()
永远不会为char 179
被消耗,我们有无限循环。
while(!feof(file))
隐藏了这个问题,但没有引起它。
@ryyker提出的fgets()
是一个很好的方法。 另一个是:
while (fscanf(file, "%dt%65[^tn]n", &artist_ID, artist_name) == 2) {
temp = create_play(artist_ID, artist_name, 0, -1);
head = add_play(head, temp);
printf("%sn", artist_name);
}
IOW,验证*scanf()
的结果。