EXC_BAD_ACCESS при попытке перезаписать NSString ComponentsSeparatedByString:

Я пишу программу Objective-C для работы с траекториями биомолекул с XCODE 4.3.1 и ARC. Мне нужно читать файлы PDB, т.е. анализировать большое количество данных в текстовом формате. Я очень разочарован неэффективностью NSString и пытался написать C-эквивалент компонентовSeparatedByString:. Алгоритм отлично работает с NSString и NSMutableArrays, но мне трудно использовать char* и char**.

К сожалению, я получаю сообщение об ошибке EXC_BAD_ACCESS. Странно то, что я получаю ошибку для i=68103 и j=68049 (эти числа звонят вам в колокол?), что означает, что он работал некоторое время перед сбоем. Ошибка "статическая" (всегда блокируется на одних и тех же (i,j) числах). Кажется, что массив работает нормально (NSLog по его значениям перед сбоем).

Как кажется, я не очень разбираюсь в C-коде и тонкостях указателей, но я определенно был бы рад услышать ваши предложения, чтобы заставить его работать! Спасибо !

Вот код:

+(char**) componentsSeparedByNewLineCEQUIV:(const char*)aChar:(int*)numWord
{ // char* aChar : my file, is typically 3 millions characters
int j=-1; //Last non space character
int i; //Scanned character
int len=strlen(aChar);

char** stringArray=malloc((*numWord)*sizeof(char*));

for (i=0;i<len; i++)
{   if (aChar[i]==10)
    {
        if ( j!=-1)
        {   
            char* buffer2=malloc(i-j+1);
            strcpy(buffer2, strndup(aChar+j, i-j));
            stringArray[i]=malloc(sizeof(char)*strlen(buffer2)+1); //EXC_BAD_ACCESS HERE
            strcpy(stringArray[i], buffer2);
        }
        j=-1;
    }
    else if (j==-1)
    {j=i;}
}
if (j!=-1)
{   char* buffer2=malloc(i-j+1);
    strcpy(buffer2, strndup(aChar+j, i-j));
    stringArray[i]=malloc(strlen(buffer2)+1);
    strcpy(stringArray[i], buffer2);
}

return stringArray;
}

person Bertrand Caron    schedule 25.09.2012    source источник
comment
Вы уверены, что у вас не заканчивается куча и вы действительно получаете ошибку в следующей строке?   -  person Hot Licks    schedule 25.09.2012
comment
Проверьте возвращаемое значение из malloc.   -  person Hot Licks    schedule 25.09.2012
comment
Мне кажется, что доминирующим фактором в стоимости выполнения этой функции будет выделение памяти, а не строки, и это также может быть фундаментальной проблемой производительности при реализации в NSString. В самом деле, вам действительно нужны копии вообще?   -  person marko    schedule 26.09.2012
comment
Что ж, рано или поздно он мне понадобится. Строка PDB выглядит так: ATOM 1 N THR 1 64,676 8,697 25,349 Рано или поздно я разберу эти параметры в структурах (АТОМЫ, ОСТАТКИ, БЕЛКИ), но вы правы, мне следует избегать многократного копирования!   -  person Bertrand Caron    schedule 26.09.2012


Ответы (4)


Вы, наверное, не первый, кто столкнулся с этой проблемой :)

Почему бы просто не использовать strtok?

PS Какой анализ показал, что проблема именно в NSString?

person deanWombourne    schedule 25.09.2012
comment
Time Profiler: я трачу огромное количество времени на методы NSString, в частности: substringWithRange: characterAtIndex: я разделил на два время, необходимое для анализа NSStrings путем преобразования в char*, анализа char* и обратного преобразования в NSString, и временной профиль показал, что преобразование (NSString stringWithString: и UTF8string) по-прежнему было основным источником ресурсов. Все эти методы были, конечно, бесконечно быстрее, чем componentSeparatedByCharacterInSet: . Я ясно (мне кажется, что нет!)? - person Bertrand Caron; 25.09.2012
comment
Кстати, strtok помог мне собрать временную рабочую версию, но мне все же интересно, может ли эта быть быстрее... - person Bertrand Caron; 26.09.2012

Я не знаю, почему ошибка находится в строке выше, где она должна быть. Однако вы копируете строку, которая не выделена.
stringArray[i] не выделяется, когда вы копируете на него буфер2, выделите его:

    if ( j!=-1)
    {   
        char* buffer2=malloc(i-j+1);
        strcpy(buffer2, strndup(aChar+j, i-j));
        stringArray[i]=malloc(sizeof(char)*strlen(buffer2)+1); //EXC_BAD_ACCESS HERE
        stringArray[i]=(char*)malloc( (strlen(buffer2)+1)*sizeof(char));  // Allocate the string
        strcpy(stringArray[i], buffer2);
    }
person Ramy Al Zuhouri    schedule 25.09.2012
comment
Я заменяю строку, и она по-прежнему блокируется под тем же номером (i=68103 и j=68049) с EXC_BAD_ACCESS... - person Bertrand Caron; 25.09.2012

Во-первых: если я не совсем ошибаюсь, но я думаю, что вы потребляете как минимум в 4 раза больше памяти, чем вам нужно:

Вы используете malloc для создания buffer2, а также используете strndup для получения нужных символов. strndup делает именно то, что вы хотите, но за один шаг. char* buffer2 = strndup(aChar+j, i-j) должно быть вашим первым шагом. Хуже того, в следующих двух строках вам необходимо сделать то же самое снова. Так что я думаю, что вы действительно хотите, это stringArray[i] = strndup(aChar+j, i-j). Чтобы посмотреть на проблемы с памятью: все функции используют errno для обозначения сбоя выделения памяти.

Во-вторых: ваши функции не возвращают количество компонентов, поэтому ваш stringArray может содержать какой-то мусор без ведома.

Третье: strlen дорого и вам это не нужно, просто используйте for(int i = 0; aChar[i] != '\0'; i++)

person Jonathan Cichon    schedule 25.09.2012

Обновление для всех, кому это может быть интересно: это рабочая версия, использующая strtok, которая может быть полезна, хотя мне все еще интересен ответ на мой код.

Этот код был протестирован в 5 раз быстрее (125 мс против 581 мс), чем [string componentSeparatedByString:@"\n"] ...

+(char**)componentsSeparatedByNewLine:(const char*)aChar:(int*)numWord
{

int i;
int j=0;
int len = strlen(aChar);
*numWord=1;
for (i=0;i<len; i++)
{
    if (aChar[i]==10) *numWord=*numWord+1;   //change 10 for any other character (ASCII for space)
}

char** stringArray=malloc((*numWord)*sizeof(char*));
char* pch;

char* aChar2=malloc(len+1);
strcpy(aChar2,aChar);

pch = strtok(aChar2,"\n");
while (pch != NULL)
{   
    stringArray[j]=(char*)malloc( (strlen(pch)+1)*sizeof(char));
    strcpy(stringArray[j], pch);
    //NSLog(@"%s",stringArray[j]);
    j=j+1;
    pch = strtok (NULL, "\n");
}
return stringArray;
}
person Bertrand Caron    schedule 26.09.2012