2016-04-08 2 views
0

Я должен загрузить список имен из файла, а затем найти эти имена во втором файле и загрузить их в структуру с некоторыми другими данными (для простоты я буду загрузить их в другой массив под названием «тест».Получение странных символов из функции strncpy()

Первая часть просто отлично, я открываю файл и загружать все имена в 2dimensional массив называется 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. 

Вопрос, почему я получаю символы, такие как «╕ & д» и «ⁿ └» во вновь созданный массив? А также есть ли еще более эффективный способ добиться того, что я пытаюсь сделать?

+1

вам нужно добавить нуль-терминатор к 'тест [J]' после strncpy –

+1

Ограничение длины для 'strncpy' должны быть основаны на целевом размере, не длина источника: это точка его использования над 'strcpy', который использует только длину источника. –

+0

для удобства понимания и удобочитаемости нами людей: 1) следовать аксиоме: * только один оператор в строке и (самое большее) одно объявление переменной для каждого оператора: * 2) отдельные блоки кода (для if, else, while, do ... while, switch, case, default) через пустую строку. – user3629249

ответ

2

Проблема заключается в том, что strncpy не сохраняет нуль в целевом массиве, если указанная длина меньше исходной строки (как это всегда имеет место здесь). Таким образом, всякий мусор, находящийся в массиве test, останется там.

Вы можете решить эту конкретную проблему обнуления test массив, либо при объявлении его:

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

или как вы его используете:

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

, но в целом, то лучше избегайте strncpy по этой причине.

+0

Непонятно, что '... strncpy (test [j], found, strlen (namesArr [i]) ;;' заставляет нулевой символ в 'test [j]'. Это было намерение? см., что 'strlen (namesArr [i]) <20' – chux

+0

@chux: yes, должен иметь strlen <= 19, так как фиксированный размер dest имеет только столько места - если он длиннее, вы переполняете буфер. причина, чтобы избежать как strcpy, так и strncpy. –

+0

'strlen()' всегда будет давать <20, поскольку 'namesArr [i]' также ограничен и не содержит строк больше чем это. Это решило проблему, спасибо! – Nicke011

0

В дополнение к тому, что сказал Крис Додд ,, цитата из man strncpy

strncpy() функция аналогична [к зЬгсру() функции], за исключением того, что в большинстве п байтов src копируются. Предупреждение: Если не существует нулевого байта среди первых n байтов src, строка, помещенная в dest, не будет заканчиваться нулем.

Поскольку параметр размера в вашем strncpy вызова является длиной строки, это будет не включают в себя нулевые байты в конце строки и, таким образом, ваша строка назначения не будет нулевой байт из этого вызова ,

2

Ограничение по длине для 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] = '\0';     // add terminator (if copy did not complete) 

ли загружен вы namesArr[] из файла правильно это еще один потенциальный вопрос, так как вы не показывают код.

+1

Согласен. Примечание: 'strncpy (test [j], found, sizeof test [j] - 1);' будет более ремонтопригодным. – chux

+0

@chux Я почти делал, но '20' был также жестко закодирован ... –

+0

Когда появляется волшебное число (например, 20), лучше иметь его только один раз и дать остальному коду использовать производные/вычисляемые значения. – chux

2

Отредактировано:

Незначительное изменение к предыдущему ответу:

1) Поскольку вы работаете со строками C, убедитесь, что (так как strncpy(...) не делает это для вас), что вы обнулить прекратить буфер.
2) При использовании strncpy аргумент length должен представлять емкость байта целевой строки - 1 (пробел для нулевого терминатора), а не длину строки источника.

... 
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; 
... 
+0

Не побеждает ли это преимущество использования 'strncpy' над' strcpy'? Размер цели не отображается нигде. –

+0

@WeatherVane - Отредактировано по адресу (по крайней мере, что я думаю), проблемы, которые вы подняли. Спасибо. – ryyker

Смежные вопросы