Динамическое объединение строк с помощью strcat в C

У меня возникла проблема при использовании strcat, однако при использовании realloc strcat перезаписывает строку назначения.

char *splitStr(char *line) {
        char *str_;
        str_ = (char *) malloc(1);
        char *ptr = strtok(line,"\n");
        int a;
        while (ptr != NULL) {
              if (ptr[0] != '$') {
                        printf("oncesi %s\n", str_);
                        a = strlen(ptr) + strlen(str_) + 1;
                        str_ = realloc(str_, a);
                        strcat(str_, ptr);
                        str_[a] = '\0';
                        printf("sontasi:%s\n", str_);
              }
              ptr = strtok(NULL, "\n");
        }
        printf("splitStr %d\n", strlen(str_));
        printf("%s", str_);
        return str_;
}

и мое входное значение ;

*4
$3
200
$4
4814
$7
SUCCESS
$4
3204

поэтому я хочу разделить это входное значение через strtok; стрток(строка,'\n');

и объединить всю строку без начального символа "$" с новым символом. Однако этот код дает следующий вывод;

line: *4
oncesi 
sontasi:*4
oncesi *4
200tasi:*4
200esi *4
4814asi:*4
4814si *4
SUCCESS:*4
SUCCESS*4
3204ESS:*4
splitStr 25

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


person Orcun    schedule 07.01.2020    source источник
comment
похоже, в вашем файле есть \r   -  person Chris Turner    schedule 07.01.2020
comment
Покажите минимально воспроизводимый пример.   -  person Jabberwocky    schedule 07.01.2020
comment
Неопределенное поведение... str_ используется, когда память, на которую он указывает, не инициализирована   -  person 4386427    schedule 07.01.2020
comment
Неопределенное поведение... str_[a]='\0'; пишет вне выделенной памяти   -  person 4386427    schedule 07.01.2020
comment
str_[a] = '\0'; неверно, так как выходит за пределы выделенной памяти. Это должно было быть str_[a-1] = '\0'; (но это необязательно, потому что этим занимается strcat). Предполагается, что строка str_ не инициализирована и вызывает неопределенное поведение, когда поток программы впервые входит в блок if. Он должен быть инициализирован как str_[0] = '\0'; сразу после malloc.   -  person Lxer Lx    schedule 07.01.2020
comment
Вы также забыли инициализировать str_ после первого malloc. Таким образом, первый strlen(str_) будет поддельным. вам нужно str_[0] = 0; сразу после malloc.   -  person Serge    schedule 07.01.2020
comment
Как указал @ChrisTurner, весьма вероятно, что ваши строки заканчиваются на \r\n, в результате чего положение курсора перемещается в первую позицию в той же строке при печати на терминале.   -  person Lxer Lx    schedule 07.01.2020


Ответы (2)


следующий предлагаемый код:

  1. чисто компилирует
  2. выполняет указанный функционал
  3. слегка переформулирован для удобочитаемости вывода
  4. проверяет наличие ошибок из malloc() и realloc()
  5. показано, как инициализировать массив str[], что является проблемой в опубликованном коде OP.
  6. функция: strlen() возвращает size_t, а не int. поэтому правильный спецификатор преобразования выходного формата: %zu
  7. не использует завершающие символы подчеркивания в именах переменных

и теперь предлагаемый код:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>


char *splitStr( char *line ) 
{
    printf("original line: %s\n", line);

    char *str = malloc(1);
    if( !str )
    {
        perror( "malloc failed" );
        exit( EXIT_FAILURE );
    }

    str[0] = '\0';   // critical statement
    char *token = strtok(line,"\n");


    while( token ) 
    {
        if( token[0] != '$') 
        {
            char* temp = realloc( str, strlen( token ) + strlen( str ) + 1 );
            if( ! temp )
            {
                perror( "realloc failed" );
                free( str );
                exit( EXIT_FAILURE );
            }

            str = temp;   // update pointer

            strcat(str, token);
            printf( "concat result: %s\n", str );
        }
        token = strtok(NULL, "\n");
    }

    printf("splitStr %zu\n", strlen(str));
    return str;
}


int main( void )
{
    char  firstStr[] = "$abcd\n$defg\nhijk\n";
    char *firstNewStr = splitStr( firstStr );
    printf( "returned: %s\n\n\n\n", firstNewStr );
    free( firstNewStr );

    char  secondStr[] = "abcd\ndefg\nhijk\n";
    char *secondNewStr = splitStr( secondStr );
    printf( "returned: %s\n\n\n\n", secondNewStr );
    free( secondNewStr );
}

выполнение предложенного кода приводит к:

original line: $abcd
$defg
hijk

concat result: hijk
splitStr 4
returned: hijk



original line: abcd
defg
hijk

concat result: abcd
concat result: abcddefg
concat result: abcddefghijk
splitStr 12
returned: abcddefghijk
person user3629249    schedule 08.01.2020
comment
Это не отвечает на вопрос ОП. Тем не менее, нет отрицательного голоса из-за лучшего кода. (Даже ваш интервал непостоянен.) - person the busybee; 08.01.2020
comment
@thebusybee, это утверждение: str[0] = '\0'; // critical statement отсутствует в опубликованном коде ОП. Я думал, что ясно дал понять это, когда разместил комментарий: // critical statement Кроме того, main() вызывает splitStr() с двумя немного разными строками из-за оператора if( token[0] != '$') в версии OP функции splitStr(), чтобы выделить оба условия этого оператора. - person user3629249; 08.01.2020

Ваш ввод содержит коды конца строки Windows/DOS "\r\n".

Поскольку strtok() просто заменяет '\n' на '\0', '\r' остается в строке. При выводе он перемещает курсор влево, а дополнительные символы перезаписывают старые символы, по крайней мере, визуально.

Однако ваша объединенная строка должна быть в порядке. Подсчитайте символы и не забудьте включить '\r' для каждой строки: "*4\r200\r4814\rSUCCESS\r3204\r" — это 25 символов, как показывает вывод splitStr 25.


Дополнительные примечания:

  • Как уже говорили другие, str_ = (char *) malloc(1); не инициализирует пространство, на которое указывает str_. Вам нужно сделать это самостоятельно, на примере str_[0] = '\0';.
  • Не используйте символы подчеркивания таким образом.
  • Вам не нужно преобразовывать результат malloc(), это void*, совместимый с char* (и любым другим).
person the busybee    schedule 08.01.2020