문자열의 일부를 다른 문자열로 교체


186

C ++에서 문자열의 일부를 다른 문자열로 바꿀 수 있습니까?

기본적 으로이 작업을 수행하고 싶습니다.

QString string("hello $name");
string.replace("$name", "Somename");

그러나 Standard C ++ 라이브러리를 사용하고 싶습니다.


1
가능한 중복 C에서 문자열을 대체하는 기능무엇입니까? 죄송합니다. C ++가 아닌 C입니다. 투표를 취소 할 수 있기를 바랍니다.
polygenelubricants

1
@poly 나는이 꺼리가 너무 ++ C에 대한 요청을받은 생각,하지만 난 그것을 찾을 수 없습니다
마이클 Mrozek

1
질문에 std 태그가 있지만 부스트 문자열 알고리즘에 관심이있을 수 있습니다. 부스트 알고리즘은 다양한 대체 알고리즘 (인플레 이스 / 복사, 대소 문자 구분 / 대소 문자 구분, 첫 번째 / 마지막 / 모두 / n 번째)을 포함합니다. ).
UncleBens

@Michael Mrozek stackoverflow.com/questions/3418231/에 하나 이상이 있지만 새롭고 여기의 replaceAll 메소드가 더 강력합니다.
dave-holm 2016 년

답변:


288

문자열 ( find) 내에서 하위 문자열을 찾는 기능 과 문자열 의 특정 범위를 다른 문자열 ( replace) 로 바꾸는 기능이 있으므로 원하는 효과를 얻기 위해 이들을 결합 할 수 있습니다.

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");

의견에 대한 응답으로 replaceAll아마도 다음과 같이 보일 것입니다.

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'
    }
}

2
원래 문자열에 "$ name"인스턴스가 두 개 이상 있고이를 모두 바꾸려면 어떻게 수정해야합니까?
Tom Leese

1
왜되지 않습니다 fromto당 통과 const참조? from없는 경우 기능 은 무엇입니까 ? -1저 한테 서요
sbi

10
@ sbi 고정, 공격 대신 권장 사항으로 표현 할 수는 있었지만 단순히 나에게 발생하지는 않았지만 거의 사용하지 않을 것이라고 생각합니다. const이와 같은 유틸리티 메소드를 작성하면 내가 알고있는 경우에만 호출합니다. 교체가 유효
Michael Mrozek

10
@Michael : 좋습니다. 저는 하향 투표를 상향 투표로 바꿨습니다. 닫기 const는 C ++ 최고의 도구 중 하나를 무시합니다. 함수 매개 변수의 기본 모드 const참조 당 전달 이어야합니다. (FTR을 사용하지 const않으면 임시 const참조 를 비 참조에 바인딩 할 수 없으므로 문자열 리터럴을 함수에 전달할 수도 없습니다 . 따라서 함수는 작성된 내용을 수행하지도 않습니다.)
sbi

19
이것이 2018 년에도 여전히 유일한 솔루션입니까? 그렇다면 C ++위원회에서이 내용을 읽고 있다면 정리하십시오. 부끄러운 일입니다. split (문자열, 문자열) 및 바꾸기 (문자열, 문자열)하십시오!
user997112

96

C ++ 11을 사용하면 다음 std::regex과 같이 사용할 수 있습니다 .

#include <regex>
...
std::string string("hello $name");
string = std::regex_replace(string, std::regex("\\$name"), "Somename");

이스케이프 문자를 이스케이프 처리하려면 이중 백 슬래시가 필요합니다.


std::regex_replaceQt의 문자열을 받아들이지 않을 것이라고 확신 합니다.
BartoszKP

1
네 말이 맞아 QString은 QRexExp를 수락하여 Qt의 자체 항목을 사용할 수있는 replace 메소드를 제공합니다. 하지만 현재 답변은로 대체 string하여 수정할 수 있다고 생각 string.toStdString()합니다.
Tom

1
또는 질문이 Qt와 관련이 없으므로 로 변경 String하면 std::string됩니다. 그 일을 고려하십시오-나중에 귀하의 답변을 기꺼이 찬송하겠습니다.
BartoszKP

5
원시 문자열은 R"(\$name)"대신 쓸 수 있습니다 "\\$name".
Jarod42

2
std :: regex의 생성 시간을 고려하지 않고 찾기 / 바꾸기보다 속도가 얼마나 느립니까?
jw_

18

std::stringreplace당신을 위해 무엇을 찾고있는 것을, 방법을 무엇입니까?

시도해 볼 수 있습니다 :

s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");

난 그냥에 대한 설명서를 읽고, 자신을 시도하지 않은 find()replace().


2
내가 볼 수 있듯이 std :: string replace 메소드는 원하는대로 두 개의 문자열을 사용하지 않습니다.
Tom Leese

2
이것은 나를 위해 작동하지 않습니다. sizeof는 string ( "Somename"). size ()-1로 대체되어야합니다.
TimZaman

@ TimZaman : 그것은 당황 스럽습니다. 문서는 분명히 C 스타일 문자열에서 초기화 할 수 있다고 명시합니다.
SC Madsen

5
두 번째 인수는 "Somename"의 길이 대신 "$ name"의 길이 여야합니다. 그렇지 않습니까?
Daniel Kiss

10

새 문자열을 반환하려면 다음을 사용하십시오.

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;
}

성능이 필요한 경우 입력 문자열을 수정하는 최적화 된 함수는 다음과 같습니다. 문자열의 복사본을 만들지 않습니다.

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();
    }
}

테스트 :

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;

산출:

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

ReplaceStringInPlace ()에서 subject.replace를 호출하면 실제로 문자열을 수정합니까?
Damian

나는 소스를 간단히 보았고 의미 체계 이동을 사용하여 이전 문자열의 앞면을 제자리로 옮기는 것처럼 보이므로 복사되지는 않지만 삽입 된 새 조각은 이전 문자열과 이전 문자열의 꼬리에 복사됩니다 이전 문자열의 크기가 조정 된 버퍼에 복사됩니다. 문자열이 너무 많이 확장되어 전체 기본 버퍼가 재 할당 될 수 있지만, 예에서와 같이 1 대 1을 바꾸면 "제자리에"있거나 복사되지 않은 것으로 생각되지만 문자열을 확장하면 이전 문자열의 첫 부분 만 복사되지 않으며, 그때만 가능합니다.
Motes

6

예, 할 수 있지만 string의 find () 멤버로 첫 번째 문자열의 위치를 ​​찾은 다음 replace () 멤버로 바꿔야합니다.

string s("hello $name");
size_type pos = s.find( "$name" );
if ( pos != string::npos ) {
   s.replace( pos, 5, "somename" );   // 5 = length( $name )
}

표준 라이브러리를 사용할 계획이라면, 이 모든 것들을 아주 잘 다루는 C ++ 표준 라이브러리 사본을 실제로 얻어야 합니다.


1
size_type이 아니라 size_t입니다.
revo

1
size_t 또는 unadorned size_type이 아닌 std :: string :: size_type입니다.
jmucchiello

5

나는 일반적으로 이것을 사용한다 :

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;
}

아무것도 찾을 std::string::find()때까지 검색된 문자열의 다른 항목을 찾기 위해 반복적으로 호출 합니다 std::string::find(). 일치 std::string::find()하는 위치 를 반환 하기 때문에 반복자를 무효화하는 데 문제가 없습니다.


4

이것은 옵션처럼 들립니다

string.replace(string.find("%s"), string("%s").size(), "Something");

이것을 함수로 포장 할 수는 있지만이 한 줄 솔루션은 받아 들일 수 있습니다. 문제는 이것이 첫 번째 발생만을 변경한다는 것입니다. 반복하고 싶을 수도 있지만 동일한 토큰 ( %s) 을 사용 하여이 문자열에 여러 변수를 삽입 할 수도 있습니다


1
스타일을 좋아하지만 다른 문자열 ^^ 혼란 발견str.replace(str.find("%s"), string("%s").size(), "Something");
폴 워츠

3

모든 문자열이 std :: string 인 경우 sizeof()C ++ 문자열이 아닌 C 문자열에 사용 되므로 문자 컷오프에 이상한 문제가 있습니다. 수정은의 .size()클래스 메소드 를 사용하는 것입니다 std::string.

sHaystack.replace(sHaystack.find(sNeedle), sNeedle.size(), sReplace);

그것은 sHaystack 인라인을 대체합니다-다시 할당 할 필요가 없습니다.

사용법 예 :

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;

2
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);

2

빠르게하려면 두 가지 스캔 방법을 사용할 수 있습니다. 의사 코드 :

  1. 첫 번째 파싱. 일치하는 문자 수를 찾으십시오.
  2. 문자열의 길이를 확장하십시오.
  3. 두 번째 파싱. 우리가 대치 할 때 문자열의 끝에서 시작합니다. 그렇지 않으면 첫 번째 문자열에서 문자를 복사합니다.

이것이 내부 알고리즘에 최적화 될 수 있는지 잘 모르겠습니다.

그리고 C ++ 11 코드 예제이지만 하나의 문자 만 검색합니다.

#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";

}

1
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;
}

2
이 코드를 (답변으로) 설명해 주시겠습니까? 그런 식으로 더 많은 투표를 얻을 수 있습니다!
모자와 사람

1

문자열을 한 번만 크기 조정하면 교체가 발생할 수 있다는 점을 고려한 자체 구현입니다.

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;
}

사용법은 예를 들어 다음과 같습니다.

std::string dirSuite = replaceAll(replaceAll(relPath.parent_path().u8string(), "\\", "/"), ":", "");

0

방금 C ++을 배우고 있지만 이전에 게시 한 코드 중 일부를 편집하면 아마도 이와 같은 것을 사용합니다. 이를 통해 하나 이상의 인스턴스를 유연하게 교체 할 수 있으며 시작점을 지정할 수도 있습니다.

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;
}

0

이것은 사용하는 것이 더 나을 수 있습니다

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);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.