Замена слова в строке на другое
функция должна иметь вид:
char *modString(char *string);
то есть функция получает строку, и возвращает строку уже с измененным словом.
пример:
исходная строка: текст всякий ватный текст
обработанная строка: "текст" всякий ватный "текст"
#include <string.h>
char *str1 = "текст";
char *str2 = "\"текст\"";
char* modString(char *string)
{
int len, len1, len2, n1, n2, i, j, k;
int *numpos;
char *result;
len = strlen(string);
len1 = strlen(str1);
len2 = strlen(str2);
n1 = 0;
numpos = (int*)malloc(sizeof(int) * (len / len1 + 1));
for(i = 0; i <= len - len1; ++i)
{
j = 0;
while(j < len1 && string[i + j] == str1[j])
++j;
if(j == len1)
{
numpos[n1++] = i;
i += len1 - 1;
}
}
if(!n1)
{
result = (char*)malloc(sizeof(char) * (len + 1));
strcpy(result, string);
}
else
{
result = (char*)malloc(sizeof(char) * (len + (len2 - len1) * n1 + 1));
for(i = 0, j = 0, n2 = 0; i < len; ++i, ++j)
if(n2 == n1 || numpos[n2] > i)
result[j] = string;
else
{
for(k = 0; k < len2; ++k, ++j)
result[j] = str2[k];
i += len1 - 1;
--j;
++n2;
}
result[j] = '\0';
}
free(numpos);
return result;
}
Но ведь интерес представляет не только заставить программу работать, но и избавить её от таких недостатков. Выявление и исправление их - это даже более интересная задача.
С ходу вижу следующее.
1. Непонятно, почему str1 и str2 не являются параметрами modString.
2. Имена этих параметров не говорят, что является строкой для поиска, а что для замены.
3. len, len1, len2, n1, n2 - названия мне не нравятся, они не говорят о себе. Такие переменные годятся только для очень коротких функций, да и то редко.
4. Память на строку выделили, а кто удалять будет? Пользователь? Откуда он знает, что должен? Правда, элегантного решения для C я сразу не могу придумать.
С ходу вижу следующее.
Высказанные (пока) замечания относятся в основном к стилистике оформления программы, либо представляются неустранимыми в силу формулировки задачи. :)
Ну это задача так поставлена:
char *modString(char *string);
то есть функция получает строку, и возвращает строку уже с измененным словом.
пример:
исходная строка: текст всякий ватный текст
обработанная строка: "текст" всякий ватный "текст"
В представленном автором прототипе функции нет переменных, в которых передавалась бы подстрока, которую в тексте нужно заменить, и подстрока, на которую нужно заменить. Поэтому я и предположил что автор эти строки хочет задавать с помощью глобальных переменных, что и сделал в своем примере. По логике, согласен, правильнее было бы так:
но автору по другому нужно...
3. len, len1, len2, n1, n2 - названия мне не нравятся, они не говорят о себе. Такие переменные годятся только для очень коротких функций, да и то редко.
Это замечания по неймингу уже, я в нем не силен (не силен в том чтобы находить переменным подходящие имена, в смысле - чтобы имя переменной более-менее соответствовало назначению)... Могу попробовать, но не факт что у меня хорошо получится:
str2 - strReplacedBy
len - originalStringLength
len1 - strReplacedLength
len2 - strReplacedByLength
n1 - posReplacingCount
n2 - posReplacingNumber
И переписанный код тогда выглядит так (сделал также чтобы заменяемая и заменяющая строка передавались как аргументы функции):
#include <string.h>
char* modString(char *string, char *strReplaced, char *strReplacedBy)
{
int originalStringLength, strReplacedLength, strReplacedByLength;
int posReplacingCount, posReplacingNumber;
int i, j, k;
int *numpos;
char *result;
originalStringLength = strlen(string);
strReplacedLength = strlen(strReplaced);
strReplacedByLength = strlen(strReplacedBy);
posReplacingCount = 0;
numpos = (int*)malloc(sizeof(int) * (originalStringLength / strReplacedLength + 1));
for(i = 0; i <= originalStringLength - strReplacedLength; ++i)
{
j = 0;
while(j < strReplacedLength && string[i + j] == strReplaced[j])
++j;
if(j == strReplacedLength)
{
numpos[posReplacingCount++] = i;
i += strReplacedLength - 1;
}
}
if(!posReplacingCount)
{
result = (char*)malloc(sizeof(char) * (originalStringLength + 1));
strcpy(result, string);
}
else
{
result = (char*)malloc(sizeof(char) * (originalStringLength + (strReplacedByLength - strReplacedLength) * posReplacingCount + 1));
for(i = 0, j = 0, posReplacingNumber = 0; i < originalStringLength; ++i, ++j)
if(posReplacingNumber == posReplacingCount || numpos[posReplacingNumber] > i)
result[j] = string;
else
{
for(k = 0; k < strReplacedByLength; ++k, ++j)
result[j] = strReplacedBy[k];
i += strReplacedLength - 1;
--j;
++posReplacingNumber;
}
result[j] = '\0';
}
free(numpos);
return result;
}
По-моему, получилось что-то страшное. :D
Да, пользователь. Если он вызвал эту функцию, присвоив результат выполнения указателю char*, то он в итоге получает строку, память под которую была выделена динамически. И если эта строка ему больше не нужна, то он в main (или в другой функции где использует modString) должен освободить с помощью free.
И варианта решения задачи, в котором соблюдался бы затребованный автором темы прототип функции и при этом не происходило бы выделения динамической памяти, я не вижу...
Как говорит Мартин Фаулер, "любой дурак может написать программу, которую поймет компилятор. Хорошие программисты пишут программы, которые смогут понять другие программисты".
Задача поставлена заменить слова. Вид функции автор задал по неопытности. Поэтому можно пренебречь этим условием.
Не особо важно, что нужно автору в данном разделе.
str2 - strReplacedBy
len - originalStringLength
len1 - strReplacedLength
len2 - strReplacedByLength
n1 - posReplacingCount
n2 - posReplacingNumber
...
По-моему, получилось что-то страшное. :D
да. Слишком длинно получилось. Ты уместил почти целый комментарий в имя. Надо нечто среднее. У меня тоже это не всегда получается (слаб в английском и т.п.), но попробую:
str2 - replacer
len - oldLen
len1 - soughtLen
len2 - replacerLen
n1 - findCount
n2 - не нужен
Варианты есть, но не скажу, что они намного лучше этого. Например, была какая-то функция Win32 API, которая делала подобную работу в 2 хода. В первом ходе она вычисляла размер требуемой строки и возвращала пользователю, во втором уже размещала данные в строку, которую пользователь предоставил.
Ходы различались тем, что в первый раз функции передавался нулевой указатель.
ИМХО если не переименовывать переменные, но добавить комментарии - описание что каждая из них обозначает, то первый вариант тоже был бы понятен и другим программистам. Функция не такая уж и большая... Хотя безусловно в правильном нейминге много пользы, но как я уже сказал - с ним у меня плохо.
Ходы различались тем, что в первый раз функции передавался нулевой указатель.
Если сделать по такому варианту, и использовать твои названия переменных (соглашусь что они гораздо лучше моих), то у меня вот что получилось. Функция принимает на вход 4 указателя:
- 1 - для записи результата (если NULL то писать ничего не надо, надо только посчитать сколько понадобится места)
- 2 - исходная строка
- 3 - заменяемая строка
- 4 - заменяющая строка
Если первый указатель не NULL, то происходит запись, но при этом ответственность за то что указатель валиден и под него выделено достаточно места, возлагается на пользователя фукнции.
#include <string.h>
int modString(char *result, char *source, char *sought, char *replacer)
{
int oldLen, soughtLen, replacerLen, findCount;
int i, j, k;
oldLen = strlen(source);
soughtLen = strlen(sought);
replacerLen = strlen(replacer);
if(result == NULL)
{
for(i = 0, findCount = 0; i <= oldLen - soughtLen; ++i)
{
j = 0;
while(j < soughtLen && source[i + j] == sought[j])
++j;
if(j == soughtLen)
{
++findCount;
i += soughtLen - 1;
}
}
return oldLen + (replacerLen - soughtLen) * findCount;
}
for(i = 0, j = 0; i <= oldLen - soughtLen; ++i, ++j)
{
k = 0;
while(k < soughtLen && source[i + k] == sought[k])
++k;
if(k == soughtLen)
{
for(k = 0; k < replacerLen; ++k, ++j)
result[j] = replacer[k];
i += soughtLen - 1;
--j;
}
else
result[j] = source;
}
result[j] = '\0';
return 0;
}
Кстати, я ещё не сильно надоел?
Фукнция int strcnmp(const char *str1, const char *str2, size_t count) выполняет лексикографическое сравнение первых count символов строк. Думаю можно создать еще один указатель и смещать его, и по нему строки сравнивать. А вместо посимвольного копирование replacer можно memcpy использовать.
Тогда так выходит:
#include <string.h>
int modString(char *result, char *source, char *sought, char *replacer)
{
int oldLen, soughtLen, replacerLen, findCount;
char *ptrSource;
char *ptrResult;
oldLen = strlen(source);
soughtLen = strlen(sought);
replacerLen = strlen(replacer);
if(result == NULL)
{
for(ptrSource = source, findCount = 0; ptrSource <= source + (oldLen - soughtLen); ++ptrSource)
if(!strncmp(ptrSource, sought, soughtLen))
{
++findCount;
ptrSource += soughtLen - 1;
}
return oldLen + (replacerLen - soughtLen) * findCount;
}
for(ptrSource = source, ptrResult = result; ptrSource <= source + (oldLen - soughtLen); ++ptrSource, ++ptrResult)
if(!strncmp(ptrSource, sought, soughtLen))
{
memcpy((void*)ptrResult, (void*)replacer, sizeof(char) * replacerLen);
ptrResult += replacerLen - 1;
ptrSource += soughtLen - 1;
}
else
*ptrResult = *ptrSource;
*ptrResult = '\0';
return 0;
}
Мне - нет. :) Другим - не знаю. :rolleyes:
Update. В последних двух своих вариантах заметил ошибку, из-за которой в случае, если в конце исходной строки имеются символы в количестве до (soughtLen - 1), которые не должны быть заменены при подстановке, то эти символы не будут переписаны в строку - результат замены, хотя должны.
В предыдущем варианте:
В последнем варианте (из этого поста):
Как вариант исправления - добавить сразу после цикла for еще один цикл (прямо перед присвоением терминального символа '\0'), чтобы последние символы были дописаны.
Для предыдущего варианта:
result[j] = source;
Для последнего варианта:
*ptrResult = *ptrSource;
Но это мелочи. Возможно, тут есть более крупные промахи, но их пусть разглядывают люди более знакомые с C. А я почти доволен - могу прочитать функцию, да и короче она немного стала.