从 strncpy() 函数获取奇怪的字符

Getting strange characters from strncpy() function

我应该从一个文件中加载一个名称列表,然后在第二个文件中找到这些名称并将它们加载到一个包含其他数据的结构中(为简单起见,我将它们加载到另一个名为“测试”。

第一部分 很好,我正在打开一个文件并将所有名称加载到一个名为 namesArr 的二维数组中。 第二部分是出现意外字符的地方,我不明白为什么。这是函数的代码:

void loadStructure(void){
    char line[MAX_PL_LENGTH], *found;
    int i, j=0;
    char test[20][20];

    FILE *plotPtr=fopen(PLOT_FILE_PATH, "r");
    if (plotPtr==NULL){perror("Error 05:\nError opening a file in loadStructure function. Check the file path"); exit(-5);}
    while(fgets(line, MAX_PL_LENGTH, plotPtr)!=NULL){                   // This will load each line from a file to an array "line" until it reaches the end of file.
        for(i=0; i<numOfNames; i++){                                    // Looping through the "namesArr" array, which contains the list of 20 character names.
            if((found=strstr(line, namesArr[i]))!=NULL){                // I use strstr() to find if any of those names appear in the particular line.
                printf("** %s", found);                                 // Used of debugging.
                strncpy(test[j], found, strlen(namesArr[i])); j++;      // Copying the newly found name to test[j] (copying only the name, by defining it's length, which is calculated by strlen function).
            }
        }
    }
    fclose(plotPtr);
    printf("%s\n", test[0]);
    printf("%s\n", test[1]);
    printf("%s\n", test[2]);
}

这是我得到的输出:

...20 names were loaded from the "../Les-Mis-Names-20.txt".
** Leblanc, casting
** Fabantou seems to me to be better," went on M. Leblanc, casting
** Jondrette woman, as she stood
Leblanct╕&q
Fabantou
Jondretteⁿ  └

Process returned 0 (0x0)   execution time : 0.005 s
Press any key to continue.

问题是,为什么我在新创建的数组中得到像“╕&q”和“ⁿ └”这样的字符?还有,有没有其他更有效的方法来实现我想要做的事情?

问题在于,如果指定的长度小于源字符串(这里总是如此),strncpy 不会在目标数组中存储空值。因此,test 数组中出现的任何垃圾都将保留在那里。

您可以通过将 test 数组置零来解决此特定问题,或者在您声明它时:

char test[20][20] = { { 0 } };

或者当你使用它时:

memset(test[j], 0, 20);
strncpy(test[j], found, strlen(namesArr[i]));

但一般来说,由于这个原因,最好避免strncpy

除了 Chris Dodd 所说的,引用自 man strncpy

The strncpy() function is similar [to the strcpy() function], except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.

由于您的 strncpy 调用中的大小参数是字符串的长度,因此这 不会 在字符串的末尾包含空字节,因此您的此调用不会以空字符终止目标字符串。

strncpy 的长度限制应基于目标大小,而不是源长度:这就是在仅使用源长度的 strcpy 上使用它的要点。在你的代码中

strncpy(test[j], found, strlen(namesArr[i]));

长度参数来自源数组,这违背了使用strncpy的目的。此外,如果函数复制字节的完整限制,则 nul 终止符将不存在,因此代码应为

strncpy(test[j], found, 19);        // limit to target size, leaving room for terminator
test[j][19] = '[=11=]';                 // add terminator (if copy did not complete)

您是否从文件中正确加载 namesArr[] 是另一个潜在的问题,因为您没有显示代码。

已编辑:

对先前答案的轻微修改:

1) 由于您正在使用 C 字符串,因此请确保(因为 strncpy(...) 不会为您做这件事)您空终止缓冲区。
2) 当使用 strncpy 时,长度参数应该表示目标字符串字节容量 - 1(space 表示空终止符),而不是源字符串长度。

...
int len = strlen(found)
memset(test[j], 0, 20);
strncpy(test[j], found, 19);//maximum length (19) matches array size 
                            //of target string -1 ( test[j] ).
if(len > 19) len = 19; //in case length of found is longer than the target string.
test[j][len+1] = 0;
...