我正在编写一个程序,它读取的文件大约有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 エリス レジーナ/アントニオ カルロス ジョビン
上面是一个示例,其中包含文件中的一些项目(不是某些行不遵循特定格式)。我的程序工作文件,直到它从示例到达最后一行,然后它无休止地打印エリス レジーナ/アントニオ カルロス ジョビ\343\203
。
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, "%d\t%65[^\t\n]\n", &artist_ID, artist_name);
temp = create_play(artist_ID, artist_name, 0, -1);
head = add_play(head, temp);
printf("%s\n", artist_name);
}
fclose(file);
//print_plays(head);
return head;
}
以上是我从该文件中读取的代码。你能帮忙解释一下是什么问题吗?
答案 0 :(得分:3)
正如评论所示,一个问题是 while(!feof(file)) 链接的内容会详细解释为什么这不是一个好主意,但总的来说,引用链接中的一个答案:
(!FEOF(文件))...
...是错误的,因为它测试了某些东西 无关紧要,无法测试你需要知道的事情。该 结果是你错误地执行了假定它的代码 正在访问已成功读取的数据,实际上这从未 发生了。 - Kerrek SB
在您的情况下,此用法不会导致您的问题,但正如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 and 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
&#34; wchar_t的&#34; (宽字符)类型,但不幸的是要避免 因为有些编译器只分配了16位 - 不足以表示 Unicode格式。无论你需要传递个性, 改变&#34; char&#34; to&#34; unsigned int&#34;或类似的。唯一剩下的用途 &#34; char&#34; type是指&#34; byte&#34;。
<强> 编辑: 强>
在上面的注释中,您声明了,但它失败的字符串是66字节长。因为你正在读一个字母&#39;数组,在包括最后一个必要字节之前,完成字符所需的字节被截断一个字节。 ASCII字符可以包含在单个char
空间中。日文字符不能。如果您使用unsigned int
数组而不是char
数组,则会包含最后一个字节。
答案 1 :(得分:3)
OP的代码失败,因为未检查fscanf()
的结果。
fscanf(file, "%d\t%65[^\t\n]\n", &artist_ID, artist_name);
fscanf()
在char
的{{1}}中读取"エリス レジーナ/アントニオ カルロス ジョビン"
。然而,这个以UTF8编码的字符串长度为66.最后'ン'
是代码227,131,179(八进制343 203 263),只有最后2个被读取。打印artist_name
时,会显示以下内容。
エリス レジーナ/アントニオ カルロス ジョビ\343\203
现在开始出现问题。 char 179
中的最后一个file
仍然存在。在 next fscanf()
上,由于char 179
未转换为int
("%d"
),因此失败。因此,fscanf()
返回0.由于代码未检查fscanf()
的结果,因此它未实现artist_ID
和artist_name
之前遗留下来,因此打印相同的文本。
由于feof()
永远不会被char 179
消耗,因此我们有无限循环。
while(!feof(file))
隐藏了这个问题,但没有引起它。
@ryyker提出的fgets()
是一种很好的方法。另一个是:
while (fscanf(file, "%d\t%65[^\t\n]\n", &artist_ID, artist_name) == 2) {
temp = create_play(artist_ID, artist_name, 0, -1);
head = add_play(head, temp);
printf("%s\n", artist_name);
}
IOWs,验证*scanf()
的结果。