我有一个文本文件,每行都有一对单词,我用它来阅读它们:
for (i=0, j=0; (c=fgetc(fp))!=EOF; i++){
if (c == ' '){
pares[j].par1[i] = '\0';
for (i=0; (c=fgetc(fp))!= '\n'; i++){
pares[j].par2[i] = c;
}
j++;
i= -1;
}
else{
pares[j].par1[i]=c;
}
}
n_pares = j;
fclose(fp);
“pares”是一个具有第一个单词(par1)和第二个单词(par2)的结构,我知道我不需要这样做。但我不知道这是否是最有效的方法。因为在python中我只使用函数split(),我不需要做for循环。谁能告诉我是否有更有效的方法?
答案 0 :(得分:0)
不是逐个字符地阅读,一般分配一个大的行缓冲区,将该行读入该缓冲区,并将其处理成更合适的大小,通常更有效。
char line[1024];
while(fgets(line, 1024, fp) != NULL) {
...now process line...
}
在C语言中分割一行比在高级语言中更难。标准函数是strtok
(字符串标记),使用起来有点搞笑。
const char sep[] = " \t\n";
for(
char *token = strtok( line, sep );
token != NULL;
token = strtok( NULL, sep )
) {
printf("%s ", token);
}
printf("\n");
strtok
在查看字符串时会记住它的位置,但它一次只能处理一个字符串。将其传递给字符串会将其重置为开头。因此,strtok( line, sep )
会在line
上启动此过程,而strtok( NULL, sep )
会在line
上获取下一个令牌,直到没有任何一个为止。
所有对strtok
的调用都会共享此全局状态,因此在使用strtok
时调用其他函数甚至不安全,该函数也可能会调用strtok
和重置状态。
非标准strtok_r
更安全,它使用变量来记住它的位置。 strsep
具有更好的界面,但它也是非标准的。
这些都可以通过修改迭代的字符串来完成。 token
只是指向line
的指针。例如,如果我们有这样的事情:
// Let's say line is pointing at memory location 1000
// 'U' is at 1000, 'p' is at 1001, ' ' is at 1002, etc...
line = 1000
|
v
"Up down\n\0"
第一次拨打char *token = strtok( line, sep );
会导致此问题。
line = 1000
|
v
"Up\0down\n\0"
^
|
token = 1000
token
指向字符串的开头,但请注意该空格已被空字节替换。这使您可以使用token
作为字符串"Up"
,而无需分配新的内存。请注意,字符串已被修改!
第二个token = strtok( NULL, sep )
导致token
移动。
line = 1000
|
v
"Up\0down\0\0"
^
|
token = 1003
最后的换行符已替换为空字节。现在token
包含字符串"down"
,但它只是指向与line
共享的内存。如果您打印line
,则会获得"Up"
。
在最后的token = strtok( NULL, sep )
上,token
将设置为NULL,因为不再有非分隔符。 line
仍在修改中。
line = 1000
|
v
"Up\0down\0\0"
token = NULL
重点是,你不能存储token
因为它指向line
而line
将改变循环的下一次迭代。你必须制作一个字符串token
的副本,指向strdup
之类的东西。这是非常节省内存的,它避免了不必要的内存分配和复制,但如果你来自更高级别的语言,可能有点难以理解。