
В языке C отсутствует встроенная функция, которая напрямую заменяет одну подстроку на другую. Любая такая операция сводится к работе с массивами символов, указателями и ручному управлению памятью. Это означает, что разработчик сам отвечает за поиск нужного фрагмента, расчёт размеров результирующей строки и корректное копирование данных.
Ключевой момент при замене подстроки – корректный расчёт размера новой строки. Если новая подстрока длиннее исходной, требуется выделять дополнительную память через malloc или calloc. При замене на более короткий фрагмент важно не потерять завершающий нулевой символ ‘\0’, иначе результат нельзя будет использовать как корректную C-строку.
Отдельного внимания требует ситуация с несколькими вхождениями подстроки. Простая замена первого совпадения и замена всех найденных фрагментов реализуются по-разному. В первом случае достаточно одного вызова strstr, во втором – нужен цикл с обновлением указателя и повторным поиском в оставшейся части строки.
Поиск позиции подстроки в строке с помощью strstr
Функция strstr из <string.h> выполняет поиск первого вхождения подстроки в нуль-терминированной строке и возвращает указатель на начало найденного фрагмента либо NULL, если совпадение отсутствует.
Сигнатура функции:
const char *strstr(const char *haystack, const char *needle);
Для получения числовой позиции подстроки используется арифметика указателей. Разность между адресом результата и адресом исходной строки даёт индекс первого символа совпадения (с отсчётом от нуля).
const char *text = "abc_def_ghi";
const char *sub = "def";
const char *pos = strstr(text, sub);
if (pos != NULL) {
size_t index = (size_t)(pos - text); /* index == 4 */
}
Если требуется позиция в символах для модифицируемой строки, допускается приведение к char *, однако сам указатель, возвращаемый strstr, указывает на участок исходной строки и не создаёт копию данных.
При поиске пустой подстроки (needle указывает на строку длины 0) функция возвращает адрес начала haystack. Это поведение следует учитывать при вычислении позиции, чтобы не получить ложный нулевой индекс.
Поиск чувствителен к регистру и выполняется слева направо. Для повторного поиска следующих вхождений используют смещение стартового адреса:
const char *p = text;
while ((p = strstr(p, sub)) != NULL) {
size_t idx = (size_t)(p - text);
p++; /* сдвиг для поиска следующего совпадения */
}
Перед заменой подстроки проверка результата на NULL обязательна: отсутствие совпадения означает, что вычисление позиции и любые операции записи приведут к ошибкам доступа к памяти.
Проверка наличия подстроки перед выполнением замены
Перед заменой подстроки необходимо убедиться, что искомый фрагмент реально присутствует в исходной строке. В языке C стандартным способом проверки служит функция strstr, возвращающая указатель на первое вхождение либо NULL.
Минимальная проверка выполняется без изменения данных и без вычисления позиции:
if (strstr(src, target) != NULL) {
/* можно выполнять замену */
}
Игнорирование этой проверки приводит к ошибкам при расчёте длины результирующей строки и копировании памяти, особенно при использовании memcpy и strncpy.
Для алгоритмов, поддерживающих множественные замены, проверка совмещается с подсчётом количества вхождений. Это позволяет заранее вычислить точный размер буфера под новую строку и избежать переполнения:
size_t count = 0;
const char *p = src;
while ((p = strstr(p, target)) != NULL) {
count++;
p += strlen(target);
}
Если target указывает на пустую строку, strstr всегда возвращает начало исходной строки. В таких случаях замену следует блокировать явно, иначе цикл обработки станет бесконечным.
При работе с изменяемыми буферами проверка должна выполняться до любых операций записи. strstr не модифицирует данные, поэтому безопасна для предварительного анализа строки.
Для чувствительных к регистру данных дополнительная нормализация не требуется, так как strstr сравнивает байты напрямую. Если нужна регистронезависимая проверка, предварительно создают временные копии строк с приведением символов к одному регистру.
Расчёт длины новой строки после замены подстроки

Перед заменой подстроки требуется точно вычислить длину результирующей строки, так как в C память под строку выделяется вручную. Ошибка на этом этапе приводит к выходу за границы буфера или к потере части данных.
Базовая формула расчёта опирается на три значения: длину исходной строки, длину заменяемой подстроки и длину строки-замены. Также учитывается количество найденных вхождений.
Если исходная строка имеет длину L, заменяемая подстрока – T, строка-замена – R, а число вхождений равно N, итоговая длина вычисляется так:
new_len = L + N * (R - T);
Для корректного завершения строки необходимо добавить один байт под нуль-терминатор ‘\0’ при выделении памяти.
Количество вхождений определяется отдельным проходом по строке без её изменения. При поиске следует сдвигать указатель на длину подстроки, чтобы избежать повторного учёта перекрывающихся совпадений.
size_t count = 0;
const char *p = src;
while ((p = strstr(p, target)) != NULL) {
count++;
p += strlen(target);
}
Если длины T и R равны, итоговая длина совпадает с исходной, однако проверка наличия подстроки остаётся обязательной.
При отсутствии вхождений новая строка может быть либо копией исходной, либо вовсе не создаваться – это решение должно быть принято до выделения памяти.
Замена пустой подстроки недопустима: при T == 0 расчёт становится некорректным и приводит к бесконечному увеличению длины.
Выделение памяти под строку с заменённой подстрокой
После расчёта длины результирующей строки необходимо выделить память с учётом нуль-терминатора. Для этого используется malloc или calloc:
char *new_str = (char *)malloc(new_len + 1);
if (new_str == NULL) {
/* обработка ошибки выделения памяти */
}
Размер буфера равен сумме исходной длины, прироста от всех замен и одного байта для ‘\0’. При множественных замен важно использовать точное количество вхождений, чтобы не допустить переполнения.
Для безопасного копирования исходной строки и вставки подстроки-замены рекомендуется работать с указателями, сдвигая их по мере записи символов в новый буфер.
При повторном использовании памяти старый указатель следует освободить функцией free после завершения всех операций:
free(old_str);
old_str = new_str;
Использование calloc предпочтительно, если нужно гарантировать обнуление всей памяти. Это исключает случайные мусорные значения в неиспользуемых участках буфера.
При динамическом выделении памяти необходимо проверять NULL после каждого malloc или calloc и корректно обрабатывать ошибку, чтобы избежать обращения к несуществующей памяти.
Копирование части строки до найденной подстроки
Для корректной замены подстроки сначала копируется сегмент исходной строки, предшествующий найденному совпадению. Используется арифметика указателей для определения длины этого фрагмента.
const char *pos = strstr(src, target);
size_t len = pos - src;
memcpy(new_str, src, len);
char *write_ptr = new_str + len;
Здесь write_ptr указывает на позицию следующего символа, куда будет вставлена строка-замена. Такой подход исключает пересечение данных при многократных заменах.
Для наглядности порядок действий можно представить в виде таблицы:
| Действие | Описание |
|---|---|
| Определение позиции подстроки | Используется strstr для нахождения начала совпадения. |
| Вычисление длины сегмента | Разность указателей pos — src даёт количество символов до подстроки. |
| Копирование сегмента | memcpy переносит символы в новый буфер до позиции совпадения. |
| Смещение указателя записи | Установка write_ptr после скопированного сегмента для последующей вставки подстроки-замены. |
Такой метод предотвращает потерю данных и обеспечивает правильное формирование новой строки перед вставкой подстроки-замены.
Вставка новой подстроки на место найденной
После копирования сегмента строки до найденной подстроки выполняется вставка новой подстроки. Для этого используется прямое копирование символов с помощью memcpy или strncpy.
Алгоритм действий можно оформить в виде списка:
- Определить длину подстроки-замены: size_t len = strlen(replacement);
- Скопировать символы замены в буфер на позицию write_ptr:
- Сместить указатель исходной строки на длину заменяемой подстроки: src += strlen(target);
- Продолжить копирование оставшейся части исходной строки после замены.
memcpy(write_ptr, replacement, len);
write_ptr += len;
При множественных заменах:
- Повторять поиск подстроки начиная с новой позиции write_ptr или смещённого указателя исходной строки.
- Всегда проверять, что strstr возвращает ненулевой указатель перед вставкой.
- Обеспечивать корректное смещение указателей для предотвращения перекрытия данных.
После завершения вставки всех подстрок следует добавить нуль-терминатор ‘\0’ для корректного завершения новой строки:
*write_ptr = '\0';
Этот подход гарантирует точное и безопасное размещение новой подстроки на месте найденной без потери данных.
Добавление оставшейся части исходной строки

После вставки новой подстроки необходимо скопировать остаток исходной строки, находящийся после заменяемого фрагмента. Это обеспечивает целостность данных и корректное формирование результирующей строки.
Для добавления оставшейся части выполняются следующие действия:
size_t remaining_len = strlen(src);
memcpy(write_ptr, src, remaining_len);
write_ptr += remaining_len;
*write_ptr = '\0';
Здесь write_ptr указывает на текущую позицию записи в новом буфере. После копирования всех символов добавляется нуль-терминатор ‘\0’ для корректного завершения строки.
При множественных заменах остаток строки после каждого найденного совпадения копируется по аналогичной схеме, чтобы подготовить буфер для следующей вставки.
Использование strlen и memcpy гарантирует точное копирование данных без изменения исходной строки и позволяет избежать ошибок выхода за границы буфера.
Для оптимизации при длинных строках допустимо обновлять указатели исходной и результирующей строки без повторного вычисления длины, сдвигая их на количество обработанных символов.
Обработка замены нескольких вхождений подстроки

Для замены всех вхождений подстроки необходимо организовать цикл, который выполняет поиск и замену до тех пор, пока strstr возвращает ненулевой указатель.
- Определить количество вхождений для расчёта размера нового буфера:
- Выделить память под новую строку с учётом количества замен.
- Инициализировать указатель записи write_ptr на начало нового буфера.
- В цикле по каждому вхождению выполнять следующие шаги:
- Скопировать сегмент исходной строки до найденного совпадения.
- Вставить строку-замену на место подстроки.
- Сдвинуть указатель исходной строки за обработанное вхождение.
- После обработки всех вхождений скопировать оставшуюся часть исходной строки и добавить нуль-терминатор.
size_t count = 0;
const char *p = src;
while ((p = strstr(p, target)) != NULL) {
count++;
p += strlen(target);
}
Для предотвращения перекрытия данных указатели исходной и результирующей строки должны обновляться после каждой операции копирования. Это гарантирует правильное формирование новой строки.
При пустой подстроке-цели (target длиной 0) цикл должен быть заблокирован, иначе обработка станет бесконечной. Для регистронезависимого поиска создают временные копии строк с приведением символов к одному регистру.
- Использовать memcpy или strncpy для вставки подстроки.
- Обязательно проверять указатели на NULL после каждого вызова strstr.
- При множественных заменах расчёт длины нового буфера выполняется заранее для предотвращения переполнения.
Вопрос-ответ:
Как в C определить позицию подстроки внутри строки?
В языке C для поиска подстроки используется функция strstr. Она возвращает указатель на первое вхождение подстроки в строке или NULL, если совпадений нет. Чтобы получить числовой индекс, вычисляют разность указателя на найденную подстроку и указателя на начало исходной строки: index = pos — src. Такой метод позволяет точно определить положение подстроки без изменения исходной строки.
Как правильно выделить память для строки после замены подстроки?
Сначала вычисляют длину новой строки с учётом количества вхождений, длины подстроки-замены и исходной строки: new_len = L + N * (R — T). Затем выделяют память с помощью malloc или calloc, добавляя один байт для нуль-терминатора. Перед записью данных проверяют, что указатель не равен NULL. После завершения работы с буфером память освобождается через free.
Как заменить все вхождения подстроки в строке на C?
Для множественных замен используют цикл с strstr, который выполняется до тех пор, пока функция возвращает ненулевой указатель. На каждом шаге копируется сегмент до найденного совпадения, вставляется строка-замена и сдвигается указатель исходной строки. После обработки всех вхождений добавляется оставшаяся часть строки и нуль-терминатор. Такой подход предотвращает потерю данных и обеспечивает корректное формирование результирующей строки.
Что делать, если подстрока для замены пустая?
Если подстрока-цель имеет длину 0, функции поиска, такие как strstr, всегда возвращают начало строки, что может привести к бесконечному циклу при замене. В таких случаях замену следует блокировать или проверять длину подстроки до выполнения операций копирования и вставки. Это предотвращает ошибки и некорректное изменение данных.
