표준 문자열에서 검색 / 찾기 및 바꾸기는 어떻게합니까?


답변:


75

자신의 교체를 구현하지 않는 이유는 무엇입니까?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}

3
"replace"에 대한 모든 호출과 함께 여기에서 메모리를 약간 엉망으로 만들고 있습니다. "oooo ... o"에서 "o"를 제거하면 복잡도가 n²가됩니다. 더 잘할 수 있다고 생각하지만이 솔루션은 이해하기 쉽다는 장점이 있습니다.
Zonko 2011 년

1
난독 화 된 for 루프가 아니라 실제 for 루프가 아닌 이유는 무엇입니까?
Shirik

나는 '최소 놀라움'원칙을 적용하는 데 익숙합니다. For 루프는 대부분의 경우 단순 인덱스 증분 사용을위한 것입니다. 여기에서는 while 루프가 더 명확합니다.
yves Baumes 2012-08-23

1
@aldo 일반적으로 복잡성을 피하는 것이 더 낫습니다. 예를 들어 다른 응답에서 언급 한대로 regex를 사용하십시오. 그러나 필요에 따라 프로젝트 종속성을 제어 할 수 있습니다. 필요한 것을 정확히 수행하는 작은 코드 스 니펫이 더 이상 때로는 더 낫습니다.
yves Baumes

158
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");

다음은 replace_all에 대한 공식 문서 입니다.


1
패턴 및 교체를 위해 명시 적으로 std :: string을 생성 할 필요는 없습니다. boost :: replace_all (target, "foo", "bar");
Alexis Wilke 2011 년

4
+1,주의해야와 함께 : replace_all12.3 모든 버전의 <대한 후원 썬 스튜디오에> 1.43의 버전 세그 폴트 것
브라이언 반덴버그

3
boost임베디드 장치에서 컴파일 시간이 상당히 늘어납니다. ARMv7 쿼드 코어도 있습니다. 100 줄의 코드가 2 분 안에 부스트없이 2 초 만에 컴파일됩니다.
Piotr Kula 2015 년

4
@ppumkin : 그것은 당신의 컴파일러 (또는 빌드 설정 등)가 짜증나는 것을 의미하며 타겟 아키텍처와 관련이 없습니다.
Daniel Kamil Kozar

컴파일러가 미리 컴파일 된 헤더를 지원하는 경우 부스트를 사용할 때 사용하는 것이 좋습니다. 정말 시간이 절약됩니다.
Alexey Omelchenko

33

C ++ 11에서는 다음을 호출하여 한 줄로이 작업을 수행 할 수 있습니다 regex_replace.

#include <string>
#include <regex>

using std::string;

string do_replace( string const & in, string const & from, string const & to )
{
  return std::regex_replace( in, std::regex(from), to );
}

string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;

산출:

Removeallspaces

감사합니다. 사용하기 쉽고 기억하기 쉽습니다!
Julian Declercq 2016-06-23

또한 from정규 표현식이 될 수 있으므로 필요한 경우 더 정교한 일치 기준을 사용할 수 있습니다. 내가 보지 못하는 것은 문자를 직접 해석하는 대신 정규식 구문 분석 적용 하지 않고 이를 수행하는 방법 from입니다.
Brent Bradburn

최신 컴파일러가 필요할 수 있습니다. gcc 5.0에서는 작동했지만 gcc 4.8.4에서는 몇 가지 문제가있었습니다.
Brent Bradburn

@nobar, 그래, 내가 제대로 기억한다면 4.8.x의 정규식 지원이 완료되지 않았습니다. 또한 더 정교한 검색을 할 수 있지만 시간면에서 페널티를받습니다. 다른보다 간단한 검색 및 바꾸기 기능보다 느릴 것입니다.
Alexis Wilke

2
이것은 매우 기본적인 영숫자 문자에 대해서만 작동하며 문자열 유형에 따라 많은 전처리를 수행하지 않고는 아무것도 작동하지 않습니다. 아직 범용 정규식 기반 문자열 대체를 찾지 못했습니다.
Piyush Soni

17

수정 된 문자열을 반환하지 않는 이유는 무엇입니까?

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 changed: " 
          << 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

6

내 템플릿 인라인 내부 찾기 및 바꾸기 :

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

대체 된 항목 수를 반환합니다 (연속적으로 실행하려는 경우 등에 사용). 그것을 사용하려면 :

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");

4
이 샘플을 GCC에서 시도했지만 컴파일되지 않았습니다. T :: size_t 사용이 마음에 들지 않았습니다. T :: size_t를 typename T :: size_type으로 바꾸면 문제가 해결됩니다.
Andrew Wyatt 2011

3

가장 쉬운 방법 (작성한 내용에 가까운 것을 제공)은 Boost.Regex , 특히 regex_replace 를 사용하는 입니다.

std :: string에는 find () 및 replace () 메서드가 내장되어 있지만 인덱스 및 문자열 길이를 처리해야하므로 작업하기가 더 복잡합니다.


3
replace_all을 포함한 부스트 문자열 알고리즘도 있습니다 (이러한 간단한 대체에는 정규식이 약간 무거울 수 있습니다).
UncleBens

3

나는 이것이 효과가있을 것이라고 믿는다. const char *를 매개 변수로 사용합니다.

//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
   //ASSERT(find != NULL);
   //ASSERT(replace != NULL);
   size_t findLen = strlen(find);
   size_t replaceLen = strlen(replace);
   size_t pos = 0;

   //search for the next occurrence of find within source
   while ((pos = source.find(find, pos)) != std::string::npos)
   {
      //replace the found string with the replacement
      source.replace( pos, findLen, replace );

      //the next line keeps you from searching your replace string, 
      //so your could replace "hello" with "hello world" 
      //and not have it blow chunks.
      pos += replaceLen; 
   }
}

점을 감안 size_type문자열 인에 대한 unsigned당신의 >=루프 조건에서 검사가 항상있을 것입니다 true. std::string::npos거기 에서 사용해야 합니다.
Pavel Minaev

size_type은 부호가 없습니다. 많은 플랫폼에서 서명되지 않았지만 전부는 아닙니다.
Alan

12
왜 이것이 std :: string의 일부가 아닌가? 프로그래밍 세계에 '찾기 및 바꾸기'작업을 제공하지 않는 다른 심각한 String 클래스가 있습니까? 확실히 그것은 두 개의 반복자를 가지고 그들 사이의 텍스트를 바꾸고 싶은 것보다 더 일반적입니까 ?? 때때로 std :: string은 가변 스펙트럼 윈드 실드가있는 자동차처럼 느껴지지만 운전자의 창을 굴릴 방법이 없습니다.
Spike0xff 2009

Spike0xff 부스트가있다 @roll_down_window
ta.speot.is

1
@gustafr : 내 실수. 이전 컴파일러가 size_t를 부적절하게 정의한 시스템에서 작업했습니다.
Alan

1
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates 
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr, 
    const std::string& replacer)
{
    // Prevent an infinite loop if the input is empty
    if (searchStr == "") {
        return str;
    }

    std::string result = "";
    size_t pos = 0;
    size_t pos2 = str.find(searchStr, pos);

    while (pos2 != std::string::npos) {
        result += str.substr(pos, pos2-pos) + replacer;
        pos = pos2 + searchStr.length();
        pos2 = str.find(searchStr, pos);
    }

    result += str.substr(pos, str.length()-pos);
    return result;
}

1
우리는 마지막 매치에서 새로운 매치를 검색하기 만하면됩니다. 이것이 알고리즘이 pos에서 마지막 매치를주의 깊게 추적하는 이유입니다. pos2는 항상 다음 일치 항목을 저장하므로 pos와 pos2 사이의 문자열을 결과에 연결 한 다음 pos와 pos2를 진행합니다. 다른 일치 항목이 없으면 나머지 문자열을 결과에 연결합니다.
Björn Ganster

1
#include <string>

using std::string;

void myReplace(string& str,
               const string& oldStr,
               const string& newStr) {
  if (oldStr.empty()) {
    return;
  }

  for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
    str.replace(pos, oldStr.length(), newStr);
    pos += newStr.length();
  }
}

oldStr이 비어 있는지 확인하는 것이 중요합니다. 어떤 이유로 든 해당 매개 변수가 비어 있으면 무한 루프에 갇히게됩니다.

하지만 가능하다면 시도되고 테스트 된 C ++ 11 또는 Boost 솔루션을 사용하십시오.

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