LINUX.ORG.RU

Косяк с функцией замены

 


0

1

Написал функцию замены подстроки в строке. На на русских (в кодировке UTF8) символах падает при попытке free'шнуть результирующую строку. На английских символах все норм.

char * strrep(char ** line, char * pattern, char * replacer)
{    
    if (!(*line) || (!pattern) || (!replacer))
        return NULL;

    if (strstr(replacer, pattern))
        return NULL;

    uint32_t lpat = strlen(pattern);
    uint32_t lrep = strlen(replacer);

    char * pcharpat;
    while ((pcharpat = strstr(*line, pattern)) != NULL)
    {
        uint32_t ppat = pcharpat - *line;

        uint32_t llin = strlen(*line);

        char * attempt = realloc(*line, llin + (lrep - lpat));
        if (!attempt)
            return NULL;

        *line = attempt;

        memmove(*line + ppat + lrep, *line + ppat + lpat,
                llin - ppat - lpat + 1);
        memcpy(*line + ppat, replacer, lrep);
    }
    return *line;
}
Где у меня косяк?

  • Тестовая строка для *line: %CAPTION%
  • Тестовая строка для pattern: %CAPTION%
  • Тестовая строка для replace: Ð\232нопка
★★

Последнее исправление: cetjs2 (всего исправлений: 3)

realloc(*line, llin + (lrep - lpat))

Может

realloc(*line, llin + (lrep - lpat) + 1)
Для '\0'.

Вообще valgrind для такого есть.

xaizek ★★★★★
()
Ответ на: комментарий от beastie

strlen отрабатывает правильно, в нем косяков нет. Он уже давно умеет корректно определять длину строк с со всеми ASCII символами.

Я чувствую, что у меня косяк с memmove, но не вижу, где.

sambist ★★
() автор топика

на русских (в кодировке UTF8

Где у меня косяк?

Сколько байт занимает один кириллический символ в UTF8?

Stil ★★★★★
()
Ответ на: комментарий от xaizek

Спасибо, ошибка была действительно в этом.

sambist ★★
() автор топика
Ответ на: комментарий от sambist

strlen работает не с символами, а с байтами, ему все равно.

И что?

Хорошо, в какую последовательность байт превратится 'И'?

Stil ★★★★★
()
Ответ на: комментарий от ilammy

Тогда он зареалочит меньше чем было - результат один и тот же - выделит столько сколько надо.

А вот если «llin + (lrep - lpat)» в итоге выйдет отрицательное, то да, будет весело ))

deep-purple ★★★★★
()
Последнее исправление: deep-purple (всего исправлений: 2)
Ответ на: комментарий от deep-purple

Во-первых, нехорошо делать такие вычитания с unsigned-типами (оно-то работает благодаря дополнительному коду, но всё же). Во-вторых, реаллокнув в меньшую сторону потеряется хвост строки, который возможно надо было ещё перемещать.

ilammy ★★★
()
Ответ на: комментарий от sambist

Ну, если *line будет указывать на короткую строку (уже в конце цикла) тогда llin будет с малым значением. Может случиться.

deep-purple ★★★★★
()
Ответ на: комментарий от ilammy

Я бы вообще не реалочил, а замалочил бы новый кусок и спокойно б с ним работал, а перед выходом сделал бы фри старого, и вернул указатель на новый.

deep-purple ★★★★★
()
Ответ на: комментарий от ilammy

Во-первых, нехорошо делать такие вычитания с unsigned-типами (оно-то работает благодаря дополнительному коду, но всё же). Во-вторых, реаллокнув в меньшую сторону потеряется хвост строки, который возможно надо было ещё перемещать.

Сейчас поменяю на int32_t.

И поменяю на malloc. Правда после 12тичасовой смены это трудновато сделать с ходу.

sambist ★★
() автор топика
Ответ на: комментарий от sambist

Сейчас поменяю на int32_t.

Тогда уж сразу size_t и ptrdiff_t. В случае с отдельным буфером вычитать ничего не придётся.

ilammy ★★★
()
Ответ на: комментарий от ilammy

В случае с отдельным буфером вычитать ничего не придётся.

Как не придется? А размер буфера и позици для трех memcpy?

sambist ★★
() автор топика
Ответ на: комментарий от deep-purple

Просто по идее strstr не пропустит строку, которая не будет содержать как минимум 1 паттерн. По крайней мере я так задумывал.

sambist ★★
() автор топика
Ответ на: комментарий от sambist

В общем вот. Может кому пригодится:

char * strrep(char ** line, char * pattern, char * replacer)
{    
    if (!(*line) || (!pattern) || (!replacer))
        return NULL;

    if (strstr(replacer, pattern))
        return NULL;

    int32_t lpat = strlen(pattern);
    int32_t lrep = strlen(replacer);

    char * pcharpat;
    while ((pcharpat = strstr(*line, pattern)) != NULL)
    {
        int32_t ppat = pcharpat - *line;

        int32_t llin = strlen(*line);

        char * attempt = malloc(llin + (lrep - lpat) + 1);
        if (!attempt)
            return NULL;

        memcpy(attempt, *line, ppat);
        memcpy(attempt + ppat, replacer, lrep);
        memcpy(attempt + ppat + lrep, *line + ppat + lpat, llin - ppat - lpat + 1);

        *line = attempt;
    }
    return *line;
}
sambist ★★
() автор топика
Ответ на: комментарий от beastie

Ему не нужно итерироваться строго по символу переменной ширины, значит поддержка «широких» символов ему не нужна. А wchar_t тем более, поскольку вне стандарта и на разных платформах он разной ширины.

andreyu ★★★★★
()
Ответ на: комментарий от sambist

strlen отрабатывает правильно, в нем косяков нет. Он уже давно умеет корректно определять длину строк с со всеми ASCII символами.

Он всегда умел работать с utf-8 строками. Только возвращал длину строки не в символах, а байтах.

andreyu ★★★★★
()
Ответ на: комментарий от Stil

Хорошо, в какую последовательность байт превратится 'И'?

Какое это имеет значение? Для «нуль-терминированного И» strlen вернет 2. О какой проблеме вы говорите?

andreyu ★★★★★
()
Ответ на: комментарий от ilammy

Сейчас поменяю на int32_t.

Тогда уж сразу size_t

Он беззнаковый, если ТСу нужен знаковый, то есть ssize_t.

andreyu ★★★★★
()

Угадай, что я тебе могу посоветовать.

Eddy_Em ☆☆☆☆☆
()

char ** line

Ух, ē-моē! Как все запущено!

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от sambist

Кстати, по поводу «search&replace» — год назад у нас был маленький конкурс на эту тему:

https://github.com/dim13/lor-contest

конкурс по си

Задача была похожая (убрать подстроку) — можешь почерпать от туда идеи.

beastie ★★★★★
()
Ответ на: комментарий от beastie

Тьфу ты! И как я сразу не подумал в содержимое глянуть?

Eddy_Em ☆☆☆☆☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.