读一个每行两个字的文件,并用C高效地保存这些字

读一个每行两个字的文件,并用C高效地保存这些字,c,C,我有一个文本文件,每行有一对单词,我用它来阅读它们: 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;

我有一个文本文件,每行有一对单词,我用它来阅读它们:

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循环。有谁能告诉我有没有更有效的方法吗?

与逐字读取相比,通常更有效的方法是分配一个大的行缓冲区,将行读入该缓冲区,并将其处理为大小更合适的块

char line[1024];
while(fgets(line, 1024, fp) != NULL) {
    ...now process line...
}
在C语言中拆分一行比在高级语言中要困难一些。标准函数是(字符串标记),使用起来有点滑稽

    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
并重置状态

非标准更安全,它使用一个变量来记住它在哪里。有更好的界面,但它也是非标准的

这些都是通过修改正在迭代的字符串来实现的<代码>标记只是指向
的指针。例如,如果我们有这样的东西:

// 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
标记
指向字符串的开头,但请注意,空格已替换为空字节。这使您可以使用
令牌
作为字符串
“Up”
,而无需分配新内存。请注意,字符串已修改

第二个
token=strtok(NULL,sep)
导致
token
移动

line = 1000
 |
 v
"Up\0down\0\0"
     ^
     |
    token = 1003
结尾的换行符已替换为空字节。现在
token
包含字符串
“down”
,但它只是指向与
行共享的内存。如果您打印
,您将得到
“向上”

在最后一个
token=strtok(NULL,sep)
上,
token
将被设置为NULL,因为不再有非分隔符字符<代码>行
保持修改状态

line = 1000
 |
 v
"Up\0down\0\0"

token = NULL

关键是,您不能存储
标记
,因为它指向
,并且
将更改循环的下一次迭代。您必须复制
标记
所指向的字符串,例如。这非常节省内存,避免了不必要的内存分配和复制,但如果您来自更高级别的语言,则可能会有点难以理解。

通常,一次分配一个大的行缓冲区,将行读入该缓冲区,而不是逐字符读取,效率更高,并将其加工成尺寸更合适的零件

char line[1024];
while(fgets(line, 1024, fp) != NULL) {
    ...now process line...
}
在C语言中拆分一行比在高级语言中要困难一些。标准函数是(字符串标记),使用起来有点滑稽

    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
并重置状态

非标准更安全,它使用一个变量来记住它在哪里。有更好的界面,但它也是非标准的

这些都是通过修改正在迭代的字符串来实现的<代码>标记
只是指向
的指针。例如,如果我们有这样的东西:

// 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
标记
指向字符串的开头,但请注意,空格已替换为空字节。这使您可以使用
令牌
作为字符串
“Up”
,而无需分配新内存。请注意,字符串已修改

第二个
token=strtok(NULL,sep)
导致
token
移动

line = 1000
 |
 v
"Up\0down\0\0"
     ^
     |
    token = 1003
结尾的换行符已替换为空字节。现在
token
包含字符串
“down”
,但它只是指向与
行共享的内存。如果您打印
,您将得到
“向上”

在最后一个
token=strtok(NULL,sep)
上,
token
将被设置为NULL,因为不再有非分隔符字符<代码>行
保持修改状态

line = 1000
 |
 v
"Up\0down\0\0"

token = NULL

关键是,您不能存储
标记
,因为它指向
,并且
将更改循环的下一次迭代。您必须复制
标记
所指向的字符串,例如。这非常节省内存,避免了不必要的内存分配和复制,但如果您来自更高级别的语言,则可能会有点难以理解。

代码的最后两行在此不重要。请不要尝试描述结构:发布。如果代码可以工作,但您希望它更好,也许可以在CodeReview上发布?附言:我会用
fgets
阅读,用
strtok
“最有效的方法”进行拆分