Czy w C ++ jest możliwe zastąpienie części łańcucha innym łańcuchem?
Zasadniczo chciałbym to zrobić:
QString string("hello $name");
string.replace("$name", "Somename");
Chciałbym jednak użyć standardowych bibliotek C ++.
Czy w C ++ jest możliwe zastąpienie części łańcucha innym łańcuchem?
Zasadniczo chciałbym to zrobić:
QString string("hello $name");
string.replace("$name", "Somename");
Chciałbym jednak użyć standardowych bibliotek C ++.
Odpowiedzi:
Istnieje funkcja znajdowania podłańcucha w ciągu string ( find
) oraz funkcja zastępowania określonego zakresu w ciągu innym ciągiem ( replace
), dzięki czemu można je łączyć, aby uzyskać pożądany efekt:
bool replace(std::string& str, const std::string& from, const std::string& to) {
size_t start_pos = str.find(from);
if(start_pos == std::string::npos)
return false;
str.replace(start_pos, from.length(), to);
return true;
}
std::string string("hello $name");
replace(string, "$name", "Somename");
W odpowiedzi na komentarz myślę, replaceAll
że prawdopodobnie wyglądałby mniej więcej tak:
void replaceAll(std::string& str, const std::string& from, const std::string& to) {
if(from.empty())
return;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}
from
i to
przekazywane za const
odniesienia? Jaka jest twoja funkcja, jeśli jej from
nie ma? -1
ode mnie za to.
const
przyszło mi to do głowy, rzadko myślę o użyciu, a gdybym napisał taką metodę użyteczności, nazwałbym to, gdybym wiedział wymiana była ważna
const
oznacza pominięcie jednego z najlepszych narzędzi C ++. Przekazywanie na const
referencję powinno być domyślnym trybem dla parametrów funkcji. (FTR, bez znaku, nie const
można nawet przekazać literałów łańcuchowych do funkcji, ponieważ nie można powiązać tymczasowych elementów z const
Z C ++ 11 możesz używać std::regex
tak:
#include <regex>
...
std::string string("hello $name");
string = std::regex_replace(string, std::regex("\\$name"), "Somename");
Podwójny ukośnik jest wymagany do ucieczki przed znakiem ucieczki.
std::regex_replace
że nie akceptuje ciągu Qt.
string
ją string.toStdString()
.
String
na std::string
, ponieważ pytanie nie jest związane z Qt. Zastanów się nad tym - chętnie poprę twoją odpowiedź później.
R"(\$name)"
zamiast "\\$name"
.
std::string
ma replace
metodę, czy tego właśnie szukasz?
Możesz spróbować:
s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");
Sam nie próbowałem, po prostu przeczytaj dokumentację na temat find()
i replace()
.
Aby zwrócić nowy ciąg, użyj tego:
std::string ReplaceString(std::string subject, const std::string& search,
const std::string& replace) {
size_t pos = 0;
while ((pos = subject.find(search, pos)) != std::string::npos) {
subject.replace(pos, search.length(), replace);
pos += replace.length();
}
return subject;
}
Jeśli potrzebujesz wydajności, oto zoptymalizowana funkcja, która modyfikuje ciąg wejściowy, nie tworzy kopii ciągu:
void ReplaceStringInPlace(std::string& subject, const std::string& search,
const std::string& replace) {
size_t pos = 0;
while ((pos = subject.find(search, pos)) != std::string::npos) {
subject.replace(pos, search.length(), replace);
pos += replace.length();
}
}
Testy:
std::string input = "abc abc def";
std::cout << "Input string: " << input << std::endl;
std::cout << "ReplaceString() return value: "
<< ReplaceString(input, "bc", "!!") << std::endl;
std::cout << "ReplaceString() input string not modified: "
<< input << std::endl;
ReplaceStringInPlace(input, "bc", "??");
std::cout << "ReplaceStringInPlace() input string modified: "
<< input << std::endl;
Wynik:
Input string: abc abc def
ReplaceString() return value: a!! a!! def
ReplaceString() input string not modified: abc abc def
ReplaceStringInPlace() input string modified: a?? a?? def
Tak, możesz to zrobić, ale musisz znaleźć pozycję pierwszego łańcucha za pomocą elementu find () łańcucha, a następnie zastąpić go jego elementem replace ().
string s("hello $name");
size_type pos = s.find( "$name" );
if ( pos != string::npos ) {
s.replace( pos, 5, "somename" ); // 5 = length( $name )
}
Jeśli planujesz korzystać ze Standardowej Biblioteki, powinieneś naprawdę zdobyć egzemplarz książki Standardowa Biblioteka C ++, która bardzo dobrze opisuje wszystkie te rzeczy.
Używam ogólnie tego:
std::string& replace(std::string& s, const std::string& from, const std::string& to)
{
if(!from.empty())
for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size())
s.replace(pos, from.size(), to);
return s;
}
Wielokrotnie wywołuje, std::string::find()
aby zlokalizować inne wystąpienia szukanego ciągu, dopóki std::string::find()
niczego nie znajdzie. Ponieważ std::string::find()
zwraca pozycję dopasowania, nie mamy problemu z unieważnieniem iteratorów.
To brzmi jak opcja
string.replace(string.find("%s"), string("%s").size(), "Something");
Możesz zawinąć to w funkcję, ale to rozwiązanie jednowierszowe wydaje się dopuszczalne. Problem polega na tym, że zmieni to tylko pierwsze wystąpienie, możesz zapętlić go, ale pozwala także wstawić kilka zmiennych do tego ciągu za pomocą tego samego tokena ( %s
)
str.replace(str.find("%s"), string("%s").size(), "Something");
Jeśli wszystkie ciągi są std :: string, znajdziesz dziwne problemy z odcięciem znaków, jeśli są używane, sizeof()
ponieważ są przeznaczone do łańcuchów C, a nie C ++. Rozwiązaniem jest użycie .size()
metody klasy std::string
.
sHaystack.replace(sHaystack.find(sNeedle), sNeedle.size(), sReplace);
Zastępuje on wbudowany sHaystack - nie trzeba już wykonywać przypisania =.
Przykładowe użycie:
std::string sHaystack = "This is %XXX% test.";
std::string sNeedle = "%XXX%";
std::string sReplace = "my special";
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
std::cout << sHaystack << std::endl;
wstring myString = L"Hello $$ this is an example. By $$.";
wstring search = L"$$";
wstring replace = L"Tom";
for (int i = myString.find(search); i >= 0; i = myString.find(search))
myString.replace(i, search.size(), replace);
Jeśli chcesz to zrobić szybko, możesz zastosować metodę dwóch skanów. Pseudo kod:
Nie jestem pewien, czy można to zoptymalizować do miejscowego algo.
I przykład kodu C ++ 11, ale szukam tylko jednego znaku.
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
void ReplaceString(string& subject, char search, const string& replace)
{
size_t initSize = subject.size();
int count = 0;
for (auto c : subject) {
if (c == search) ++count;
}
size_t idx = subject.size()-1 + count * replace.size()-1;
subject.resize(idx + 1, '\0');
string reverseReplace{ replace };
reverse(reverseReplace.begin(), reverseReplace.end());
char *end_ptr = &subject[initSize - 1];
while (end_ptr >= &subject[0])
{
if (*end_ptr == search) {
for (auto c : reverseReplace) {
subject[idx - 1] = c;
--idx;
}
}
else {
subject[idx - 1] = *end_ptr;
--idx;
}
--end_ptr;
}
}
int main()
{
string s{ "Mr John Smith" };
ReplaceString(s, ' ', "%20");
cout << s << "\n";
}
std::string replace(std::string base, const std::string from, const std::string to) {
std::string SecureCopy = base;
for (size_t start_pos = SecureCopy.find(from); start_pos != std::string::npos; start_pos = SecureCopy.find(from,start_pos))
{
SecureCopy.replace(start_pos, from.length(), to);
}
return SecureCopy;
}
Moja własna implementacja, biorąc pod uwagę, że ciąg musi być zmieniany tylko raz, wtedy może nastąpić zamiana.
template <typename T>
std::basic_string<T> replaceAll(const std::basic_string<T>& s, const T* from, const T* to)
{
auto length = std::char_traits<T>::length;
size_t toLen = length(to), fromLen = length(from), delta = toLen - fromLen;
bool pass = false;
std::string ns = s;
size_t newLen = ns.length();
for (bool estimate : { true, false })
{
size_t pos = 0;
for (; (pos = ns.find(from, pos)) != std::string::npos; pos++)
{
if (estimate)
{
newLen += delta;
pos += fromLen;
}
else
{
ns.replace(pos, fromLen, to);
pos += delta;
}
}
if (estimate)
ns.resize(newLen);
}
return ns;
}
Użycie może być na przykład takie:
std::string dirSuite = replaceAll(replaceAll(relPath.parent_path().u8string(), "\\", "/"), ":", "");
Właśnie uczę się C ++, ale edytując część wcześniej opublikowanego kodu, prawdopodobnie użyłbym czegoś takiego. Zapewnia to elastyczność zastępowania 1 lub wielu wystąpień, a także pozwala określić punkt początkowy.
using namespace std;
// returns number of replacements made in string
long strReplace(string& str, const string& from, const string& to, size_t start = 0, long count = -1) {
if (from.empty()) return 0;
size_t startpos = str.find(from, start);
long replaceCount = 0;
while (startpos != string::npos){
str.replace(startpos, from.length(), to);
startpos += to.length();
replaceCount++;
if (count > 0 && replaceCount >= count) break;
startpos = str.find(from, startpos);
}
return replaceCount;
}
Może być jeszcze lepszy w użyciu
void replace(string& input, const string& from, const string& to)
{
while(true)
{
size_t startPosition = input.find(from);
if(startPosition == string::npos)
break;
input.replace(startPosition, from.length(), to);
}
}