Я пытался написать код на C для замены подстроки в заданной строке, следуя этому уроку. Мне удалось заставить ее работать, однако одна из проблем с этой функцией заключается в том, что она приведет к переполнению буфера, если заменяемая строка больше исходной подстроки. Я знаю, что в своей программе я буду передавать в эту функцию только динамически выделенные строки, поэтому я подумал, что могу проверить, больше ли строка, которую мы собираемся заменить, чем подстрока, и если да, то я мог бы использовать функцию realloc
для измените размер исходной строки, чтобы освободить место для строки, которую мы собираемся заменить. Первоначально казалось, что функция работает, но по какой-то причине, когда я вызываю ее несколько раз для замены разных подстрок в одной и той же строке, строка повреждается.
В приведенном ниже коде у меня есть функция GetFileContents
, которая извлекает содержимое файла с именем first_page.html
в динамически выделяемую строку, а затем возвращает указанную строку. Эта функция работает правильно и не вызывает никаких проблем. StrReplaceSubstringFirstOccurance
заменит указанную мной подстроку новой строкой, а также распечатает ее вывод и префикс Inside:
. В операторе if, где я проверяю, больше ли строка, которую я заменяю, чем подстрока, у меня есть два разных подхода к изменению размера строки. Подход 2 использует realloc
, а подход 1 предполагает выделение новой строки с помощью malloc
, копирование содержимого исходной строки в новую строку, вызов free
для исходной строки и затем создание указателя на исходную строку, указывающего на новую строку. Оба подхода создают одну и ту же проблему. Как вы можете видеть в выводе, первые два вызова StrReplaceSubstringFirstOccurance
работают успешно, однако третий вызов функции (подстрока — [[to]]
, а строка, на которую нужно заменить — NOT CUSTOM!
), внутри функции функция печатает правильную строку, однако, когда я пытаюсь распечатать строку вне функции, она показывает мусорные символы. При четвертом вызове функции функция распознает, что в исходной строке больше нет вхождений подстроки (поскольку исходная строка теперь содержит мусорные символы), и поэтому она просто выходит из функции, а мусорные символы в исходной строке все еще остаются. оставайся там.
Код:
static bool StrReplaceSubstringFirstOccurance(char* source, char* substring, char* replace) {
char* substring_occurance = strstr(source, substring);
if (substring_occurance == NULL) {
printf("No substring: %s found.\n", substring);
return false;
}
if (strlen(replace) > strlen(substring)) {
size_t new_size = strlen(source) + (strlen(replace)-strlen(substring))+1;
// Approach 1
// char* temp = malloc(new_size);
// memcpy(temp, source, strlen(source)+1);
// free(source);
// source = temp;
// Approach 2
source = realloc(source, new_size);
}
substring_occurance = strstr(source, substring);
memmove(substring_occurance + strlen(replace),
substring_occurance + strlen(substring),
strlen(substring_occurance) - strlen(substring)+1);
memcpy(substring_occurance, replace, strlen(replace));
printf("\nInside: %s\n", source);
return true;
}
int main(void) {
char* first_page = GetFileContents("first_page.html");
StrReplaceSubstringFirstOccurance(first_page, "[[say]]", "CUSTOM!");
StrReplaceSubstringFirstOccurance(first_page, "[[say]]", "CUSTOM!");
printf("\nFirst Print: %s\n", first_page);
StrReplaceSubstringFirstOccurance(first_page, "[[to]]", "NOT CUSTOM!");
printf("\nSecond Print: %s\n", first_page);
StrReplaceSubstringFirstOccurance(first_page, "[[to]]", "NOT CUSTOM!");
printf("\nThird Print: %s\n", first_page);
printf("Reached the end!\n");
return 0;
}
Выход:
Inside: <!DOCTYPE html>
<html lang = "en-US">
<title>CUSTOM! [[to]]</title>
<body>
[[say]]
<h1>This is a header!</h1>
[[to]]
</body>
</html>
Inside: <!DOCTYPE html>
<html lang = "en-US">
<title>CUSTOM! [[to]]</title>
<body>
CUSTOM!
<h1>This is a header!</h1>
[[to]]
</body>
</html>
First Print: <!DOCTYPE html>
<html lang = "en-US">
<title>CUSTOM! [[to]]</title>
<body>
CUSTOM!
<h1>This is a header!</h1>
[[to]]
</body>
</html>
Inside: <!DOCTYPE html>
<html lang = "en-US">
<title>CUSTOM! NOT CUSTOM!</title>
<body>
CUSTOM!
<h1>This is a header!</h1>
[[to]]
</body>
</html>
Second Print: α#F^Ñ☻
No substring: [[to]] found.
Third Print: α#F^Ñ☻
Reached the end!
первая_страница.html
<!DOCTYPE html>
<html lang = "en-US">
<title>[[say]] [[to]]</title>
<body>
[[say]]
<h1>This is a header!</h1>
[[to]]
</body>
</html>
Спасибо, это сработало! Я передал &first_page
вместо first_page
в StrReplaceSubstringFirstOccurance
, а затем разыменовал source
на один уровень всякий раз, когда использовал его в функции. Не могли бы вы написать более подробный ответ, почему это работает, поскольку я не могу понять это. Поскольку в этом случае first_page
и source
указывают на одну и ту же память, почему функция работает только тогда, когда я передаю в нее указатель на first_page
(т. е. &first_page
), а не просто передаю first_page
в нее напрямую.
Как вы хорошо знаете, C передает аргументы по значению. Поэтому, когда вы передаете first_page
функции, независимо от того, что делает функция, когда управление возвращается, first_page
остается first_page
: переменная не изменилась, пока функция выполняла свое дело. Если вы передадите адрес first_page
, функция не сможет изменить этот адрес несмотря ни на что, но она может изменить значение. В этом случае значением является адрес, но все рассуждения те же.
Без дополнительной косвенности, когда вы меняете source
внутри своей функции (с помощью realloc()
)... переменная first_page
в main()
не меняется. Они начинаются одинаково, но в какой-то момент могут стать разными.
Тогда почему first_page
изменился правильно после первых двух вызовов StrReplaceSubstringFirstOccurance
?
Вероятно, Realloc не изменил адрес source
. Память можно увеличить, но базу оставить на том же месте.
Спасибо, есть ли у вас рекомендуемые ресурсы, где я могу узнать об этом?
Извините, нет рекомендаций. Мне нравятся руководства, но они, как правило, очень и очень сухие. И, например, они не говорят вам, что вам нужна дополнительная косвенность, как в этой конкретной ситуации. Итак... пишите код и задавайте сюда любые вопросы, которые возникают, если у вас нет лучшего места (ни школьного учителя, ни опытного коллеги,...)
Давайте продолжим обсуждение в чате.
Мне удалось это исправить, следуя комментариям pmg. Я передал указатель на переменную file_path
в функцию StrReplaceSubstringFirstOccurance
.
Новый код выглядит следующим образом:
static bool StrReplaceSubstringFirstOccurance(char** source, char* substring, char* replace) {
char* substring_occurance = strstr(*source, substring);
if (substring_occurance == NULL) {
printf("No substring: %s found.\n", substring);
return false;
}
if (strlen(replace) > strlen(substring)) {
size_t new_size = strlen(*source) + (strlen(replace)-strlen(substring))+1;
*source = realloc(*source, new_size);
}
substring_occurance = strstr(*source, substring);
memmove(substring_occurance + strlen(replace),
substring_occurance + strlen(substring),
strlen(substring_occurance) - strlen(substring)+1);
memcpy(substring_occurance, replace, strlen(replace));
printf("\nInside: %s\n", *source);
return true;
}
int main(void) {
char* first_page = GetFileContents("first_page.html");
StrReplaceSubstringFirstOccurance(&first_page, "[[say]]", "CUSTOM!");
printf("\nFirst Print: %s\n", first_page);
StrReplaceSubstringFirstOccurance(&first_page, "[[to]]", "NOT CUSTOM!");
printf("\nThird Print: %s\n", first_page);
printf("Reached the end!\n");
return 0;
}
И результат:
Inside: <!DOCTYPE html>
<html lang = "en-US">
<title>CUSTOM! [[to]]</title>
<body>
[[say]]
<h1>This is a header!</h1>
[[to]]
[[say]][[to]]
</body>
</html>
First Print: <!DOCTYPE html>
<html lang = "en-US">
<title>CUSTOM! [[to]]</title>
<body>
[[say]]
<h1>This is a header!</h1>
[[to]]
[[say]][[to]]
</body>
</html>
Inside: <!DOCTYPE html>
<html lang = "en-US">
<title>CUSTOM! NOT CUSTOM!</title>
<body>
[[say]]
<h1>This is a header!</h1>
[[to]]
[[say]][[to]]
</body>
</html>
Third Print: <!DOCTYPE html>
<html lang = "en-US">
<title>CUSTOM! NOT CUSTOM!</title>
<body>
[[say]]
<h1>This is a header!</h1>
[[to]]
[[say]][[to]]
</body>
</html>
Reached the end!
Рад, что смог помочь. Это выглядит мило.
вам нужно
static bool StrReplaceSubstringFirstOccurance(char** source, ...
, чтобы перераспределенныйsource
отразился вmain()
... и, конечно, измените наStrReplaceSubstringFirstOccurance(&first_page, "[[say]]", "CUSTOM!");
внутриmain()