문자열의 단어를 어떻게 반복합니까?


2986

문자열의 단어를 반복하려고합니다.

문자열은 공백으로 구분 된 단어로 구성되어 있다고 가정 할 수 있습니다.

C 문자열 함수 나 그런 종류의 문자 조작 / 액세스에는 관심이 없습니다. 또한 답변의 효율성보다 우아함을 우선시하십시오.

내가 지금 가지고있는 가장 좋은 해결책은 다음과 같습니다.

#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main()
{
    string s = "Somewhere down the road";
    istringstream iss(s);

    do
    {
        string subs;
        iss >> subs;
        cout << "Substring: " << subs << endl;
    } while (iss);
}

더 우아한 방법이 있습니까?


617
Dude ... 우아함은 저의 책에서 "효율적인 것 같습니다"라고 말하는 멋진 방법입니다. 템플릿에 포함되어 있지 않기 때문에 C 함수와 빠른 방법을 사용하여 무엇이든 달성하는 데 주저하지 말고;)

14
while (iss) { string subs; iss >> subs; cout << "Substring: " << sub << endl; }
— pyon

21
@Eduardo : 너무 잘못 ... 당신은 즉, 다른 값을 스트리밍을 시도하고 그 값을 사용하여 사이에 테스트 ISS에 필요string sub; while (iss >> sub) cout << "Substring: " << sub << '\n';
— 토니 델로이을

9
기본적으로이를 수행하기위한 C ++의 다양한 옵션 : cplusplus.com/faq/sequences/strings/split
— hB0

14
단순한 효율성 이상의 우아함이 있습니다. 우아한 특성에는 줄 수 및 가독성이 포함됩니다. IMHO Elegance는 효율성을 대변하는 것이 아니라 유지 관리 성을 제공합니다.
— Matt

답변:


1369

가치있는 것을 위해 표준 라이브러리 기능에만 의존하여 입력 문자열에서 토큰을 추출하는 또 다른 방법이 있습니다. STL 디자인의 힘과 우아함의 예입니다.

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>

int main() {
    using namespace std;
    string sentence = "And I feel fine...";
    istringstream iss(sentence);
    copy(istream_iterator<string>(iss),
         istream_iterator<string>(),
         ostream_iterator<string>(cout, "\n"));
}

추출 된 토큰을 출력 스트림에 복사하는 대신 동일한 일반 copy알고리즘을 사용하여 컨테이너에 삽입 할 수 있습니다 .

vector<string> tokens;
copy(istream_iterator<string>(iss),
     istream_iterator<string>(),
     back_inserter(tokens));

... 또는 vector직접 작성하십시오 .

vector<string> tokens{istream_iterator<string>{iss},
                      istream_iterator<string>{}};

164
이것에 대한 구분자를 지정할 수 있습니까? 예를 들어 쉼표로 나누는 것과 같이?
— l3dx

15
@Jonathan : \ n이 경우 구분 기호가 아니며 cout으로 출력하기위한 분리 자입니다.
— huy February

772
이것은 다른 구분 기호를 사용하지 않으므로 확장 가능하지 않고 메인 테이블이 아니기 때문에 좋지 않은 솔루션입니다.
— HelloWorld

37
실제로 이것은 다른 구분 기호와 함께 잘 작동 할 수 있습니다 (일부는 약간 추악하지만). 원하는 분리 문자를 공백으로 분류하는 ctype 패싯을 작성하고, 해당 패싯을 포함하는 로케일을 작성한 다음, 문자열을 추출하기 전에 해당 로케일로 문자열 스트림을 삽입하십시오.
— Jerry Coffin

53
@Kinderchocolate "문자열은 공백으로 분리 된 단어로 구성되어 있다고 가정 할 수 있습니다" -흠, 질문의 문제에 대한 나쁜 해결책처럼 들리지 않습니다. "확장 가능하지 않고 메인 테이블이 아닙니다" -아, 좋습니다.
— Christian Rau

2426

이것을 사용하여 문자열을 구분 기호로 나눕니다. 첫 번째는 결과를 미리 구성된 벡터에 넣고 두 번째는 새 벡터를 반환합니다.

#include <string>
#include <sstream>
#include <vector>
#include <iterator>

template <typename Out>
void split(const std::string &s, char delim, Out result) {
    std::istringstream iss(s);
    std::string item;
    while (std::getline(iss, item, delim)) {
        *result++ = item;
    }
}

std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, std::back_inserter(elems));
    return elems;
}

이 솔루션은 빈 토큰을 건너 뛰지 않으므로 다음은 4 개의 항목을 찾게되며 그 중 하나는 비어 있습니다.

std::vector<std::string> x = split("one:two::three", ':');

86
빈 토큰을 건너 뛰지 않으려면 다음을 empty()확인하십시오.if (!item.empty()) elems.push_back(item)
— 0x499602D2

11
delim은 두 개의 문자를 포함 ->합니까?
— herohuyongtao

7
@ herohuyongtao 에서이 솔루션은 단일 문자 구분 기호에만 사용할 수 있습니다.
— Evan Teran

4
@JeshwanthKumarNK는 필요하지 않지만 원하는 경우 f(split(s, d, v))사전 할당 된 이점을 유지하면서 결과를 다음과 같은 함수에 직접 전달하는 등의 작업을 수행 할 수 있습니다 vector.
— Evan Teran

8
경고 : split ( "one : two :: three", ':') 및 split ( "one : two :: three :", ':')은 동일한 값을 반환합니다.
— dshin

834

Boost를 사용하여 가능한 해결책은 다음과 같습니다.

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

이 방법은 방법보다 훨씬 빠를 수 있습니다 stringstream. 그리고 이것은 일반적인 템플릿 함수이기 때문에 모든 종류의 구분자를 사용하여 다른 유형의 문자열 (wchar 등 또는 UTF-8)을 분할하는 데 사용할 수 있습니다.

자세한 내용은 설명서 를 참조하십시오.


35
두 경우 모두 strtok와 유사한 기능보다 속도가 느리므로 속도는 관련이 없습니다.
— Tom

45
그리고 이미이 : 1,000 파일을 통해 부스트 ... BCP 사본이없는 사람들을 위해
— 로마 Starkov

12
빈 문자열 ( "")이 주어지면이 메서드는 ""문자열이 포함 된 벡터를 반환합니다. 따라서 분할하기 전에 "if (! string_to_split.empty ())"를 추가하십시오.
— Offirmo

29
@Ian Embedded 개발자가 모두 부스트를 사용하는 것은 아닙니다.
— ACK_stoverflow

31
부록으로 : 부스트를 사용해야 할 때만 부스트를 사용합니다. 일반적으로 독립적이고 이식 가능한 코드 라이브러리에 추가하여 작은 특정 코드를 얻을 수 있으므로 주어진 목표를 달성합니다. 그렇게하면 코드가 공개적이지 않고 성능이 우수하며 사소하고 이식성이 있습니다. Boost는 그 자리를 가지고 있지만 토큰 화 줄에 약간의 과잉을 제안 할 것입니다. 당신은 온 집을 엔지니어링 회사로 옮겨서 벽에 새 손톱을 꽂아 그림을 걸지 않을 것입니다 .... 그들은 그것을 할 수 있습니다 아주 잘하지만 단점은 단점보다 훨씬 뛰어납니다.
— GMasucci

362
#include <vector>
#include <string>
#include <sstream>

int main()
{
    std::string str("Split me by whitespaces");
    std::string buf;                 // Have a buffer string
    std::stringstream ss(str);       // Insert the string into a stream

    std::vector<std::string> tokens; // Create vector to hold our words

    while (ss >> buf)
        tokens.push_back(buf);

    return 0;
}

12
예를 들어 쉼표로 분할 getline하는 while조건 에서 사용하는 경우 다른 구분 기호로 분할 할 수도 있습니다 while(getline(ss, buff, ',')).
— Ali

181

코드 크기의 모든 효율성을 희생하고 우아함의 일종으로 "효율적인"것을 보는 것이 좋지 않은 사람들에게는 다음과 같은 장점이 있습니다 (템플릿 컨테이너 클래스는 놀랍도록 우아하다고 생각합니다).

template < class ContainerT >
void tokenize(const std::string& str, ContainerT& tokens,
              const std::string& delimiters = " ", bool trimEmpty = false)
{
   std::string::size_type pos, lastPos = 0, length = str.length();

   using value_type = typename ContainerT::value_type;
   using size_type  = typename ContainerT::size_type;

   while(lastPos < length + 1)
   {
      pos = str.find_first_of(delimiters, lastPos);
      if(pos == std::string::npos)
      {
         pos = length;
      }

      if(pos != lastPos || !trimEmpty)
         tokens.push_back(value_type(str.data()+lastPos,
               (size_type)pos-lastPos ));

      lastPos = pos + 1;
   }
}

나는 일반적으로 std::vector<std::string>유형을 두 번째 매개 변수 ( ContainerT) 로 사용하도록 선택 하지만 직접 액세스가 필요하지 않을 list<>때보 다 훨씬 빠르며 vector<>자체 문자열 클래스를 만들고 놀라운 속도로 복사를 수행하지 않는 std::list<subString>곳 과 같은 것을 사용할 수도 있습니다 subString증가합니다.

이 페이지에서 가장 빠른 토큰 화보다 두 배 이상 빠르며 다른 것보다 거의 5 배 빠릅니다. 또한 완벽한 매개 변수 유형을 사용하면 모든 문자열 및 목록 사본을 제거하여 추가 속도를 높일 수 있습니다.

또한 결과의 (매우 비효율적 인) 반환을 수행하지는 않지만 대신 토큰을 참조로 전달하므로 원하는 경우 여러 호출을 사용하여 토큰을 작성할 수도 있습니다.

마지막으로 마지막 선택적 매개 변수를 통해 결과에서 빈 토큰을 트리밍할지 여부를 지정할 수 있습니다.

필요한 것은 std::string... 나머지는 선택 사항입니다. 스트림이나 부스트 라이브러리를 사용하지 않지만 이러한 외부 유형 중 일부를 자연스럽게 수용 할 수있을 정도로 유연합니다.


5
나는 이것의 팬이지만 g ++ (그리고 아마도 좋은 습관)의 경우 이것을 사용하는 사람은 typedef와 typename을 원할 것입니다 : typedef ContainerT Base; typedef typename Base::value_type ValueType; typedef typename ValueType::size_type SizeType; 그런 다음 value_type과 size_types를 적절히 대체하십시오.
— aws

11
템플릿 내용과 첫 번째 주석이 완전히 외국인 사람들에게는 필수 포함이 포함 된 사용 예가 아름답습니다.
— Wes Miller

3
잘 알았습니다. aws '주석의 C ++ 행을 tokenize ()의 함수 본문에 넣은 다음 tokens.push_back () 행을 편집하여 ContainerT :: value_type을 ValueType으로 변경하고 (ContainerT :: value_type :: size_type)을 ( 크기 유형). g ++가 고발 한 비트를 수정했습니다. tokenize (some_string, some_vector);로 호출하십시오.
— Wes Miller

2
샘플 데이터에 대해 몇 가지 성능 테스트를 실행하는 것 외에도 주로 다른 문자열의 오프셋 / 길이 만 참조하는 하위 문자열 클래스를 사용하여 가능한 적은 명령으로 줄이고 가능한 적은 메모리 사본으로 줄였습니다. (내 자신을 굴 렸지만 다른 구현이 있습니다). 불행히도이를 개선하기 위해 할 수있는 일이 너무 많지는 않지만 점진적으로 증가 할 수있었습니다.
— 마리우스

3
그것은 when에 대한 올바른 출력입니다 trimEmpty = true. 점을 명심 "abo"이 답변에 구분하지만 구분 문자 목록이 없습니다. 단일 구분 문자 문자열을 사용하도록 수정하는 것이 간단합니다 (로 변경 str.find_first_of해야 한다고 생각 str.find_first하지만 잘못 될 수 있습니다 ... 테스트 할 수 없음)
— Marius

158

다른 해결책이 있습니다. 컴팩트하고 합리적으로 효율적입니다.

std::vector<std::string> split(const std::string &text, char sep) {
  std::vector<std::string> tokens;
  std::size_t start = 0, end = 0;
  while ((end = text.find(sep, start)) != std::string::npos) {
    tokens.push_back(text.substr(start, end - start));
    start = end + 1;
  }
  tokens.push_back(text.substr(start));
  return tokens;
}

문자열 구분 기호, 넓은 문자열 등을 쉽게 처리 할 수 ​​있습니다.

분할 ""하면 단일 빈 문자열이 생성되고 분할 ","(즉, sep)은 두 개의 빈 문자열이됩니다.

빈 토큰을 건너 뛰도록 쉽게 확장 할 수도 있습니다.

std::vector<std::string> split(const std::string &text, char sep) {
    std::vector<std::string> tokens;
    std::size_t start = 0, end = 0;
    while ((end = text.find(sep, start)) != std::string::npos) {
        if (end != start) {
          tokens.push_back(text.substr(start, end - start));
        }
        start = end + 1;
    }
    if (end != start) {
       tokens.push_back(text.substr(start));
    }
    return tokens;
}

빈 토큰을 건너 뛰면서 여러 구분 기호로 문자열을 분할하려는 경우이 버전을 사용할 수 있습니다.

std::vector<std::string> split(const std::string& text, const std::string& delims)
{
    std::vector<std::string> tokens;
    std::size_t start = text.find_first_not_of(delims), end = 0;

    while((end = text.find_first_of(delims, start)) != std::string::npos)
    {
        tokens.push_back(text.substr(start, end - start));
        start = text.find_first_not_of(delims, end);
    }
    if(start != std::string::npos)
        tokens.push_back(text.substr(start));

    return tokens;
}

10
첫 번째 버전은 간단하며 작업을 완벽하게 수행합니다. 내가 한 유일한 변경은 결과를 매개 변수로 전달하는 대신 결과를 직접 반환하는 것입니다.
— gregschlom

2
출력은 효율성을위한 매개 변수로 전달됩니다. 결과가 반환되면 벡터의 사본 또는 힙 할당이 필요하며이 경우 힙 할당을 해제해야합니다.
— Alec Thomas

2
위의 주석에 대한 약간의 부록 : C ++ 11 이동 의미를 사용하는 경우이 함수는 페널티없이 벡터를 반환 할 수 있습니다.
— Alec Thomas

7
@AlecThomas : C ++ 11 이전에도 대부분의 컴파일러는 NRVO를 통해 리턴 카피를 최적화하지 않습니까? (어쨌든 +1; 매우 간결)
— Marcelo Cantos

11
모든 답변 중 가장 매력적이고 융통성있는 것으로 보입니다. 덜 명확한 해결책이지만 구분 기호가있는 getline과 함께. c ++ 11 표준에는 이것에 대한 내용이 없습니까? 요즘 C ++ 11은 펀치 카드를 지원합니까?
— Spacen Jasset

123

이것은 문자열을 반복하는 가장 좋아하는 방법입니다. 단어마다 원하는 것을 할 수 있습니다.

string line = "a line of text to iterate through";
string word;

istringstream iss(line, istringstream::in);

while( iss >> word )     
{
    // Do something on `word` here...
}

이 선언 가능 wordA와 char?
— abatishchev

죄송합니다 abatishchev, C ++은 제 장점이 아닙니다. 그러나 각 단어의 모든 문자를 반복하기 위해 내부 루프를 추가하는 것이 어렵지 않을 것이라고 생각합니다. 그러나 현재 전류 루프는 단어 분리를위한 공간에 달려 있다고 생각합니다. 당신은 단지 문자에 "단어"를 캐스팅 할 수있는 경우에는 모든 공간 사이에 단 하나의 문자가 있다는 것을 알고하지 않으면 ... 미안 내가 살아야 내 C ++에 브러시을 의미하고, 더 도움이 될 캔트
— gnomed

11
단어를 문자로 선언하면 공백이 아닌 모든 문자를 반복합니다. 시도하기에 충분히 간단합니다.stringstream ss("Hello World, this is*@#&$(@ a string"); char c; while(ss >> c) cout << c;
— Wayne Werner

79

이것은 C ++에서 문자열을 어떻게 토큰 화합니까? .

#include <iostream>
#include <string>
#include <boost/tokenizer.hpp>

using namespace std;
using namespace boost;

int main(int argc, char** argv)
{
    string text = "token  test\tstring";

    char_separator<char> sep(" \t");
    tokenizer<char_separator<char>> tokens(text, sep);
    for (const string& t : tokens)
    {
        cout << t << "." << endl;
    }
}

이것이 모든 토큰의 사본을 구체화합니까, 아니면 현재 토큰의 시작 및 종료 위치 만 유지합니까?
— einpoklum

66

결과를 벡터에 넣고 문자열을 delim로 지원하고 빈 값을 유지하는 것을 제어하기 때문에 다음을 좋아합니다. 그러나 그때는 좋지 않습니다.

#include <ostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

vector<string> split(const string& s, const string& delim, const bool keep_empty = true) {
    vector<string> result;
    if (delim.empty()) {
        result.push_back(s);
        return result;
    }
    string::const_iterator substart = s.begin(), subend;
    while (true) {
        subend = search(substart, s.end(), delim.begin(), delim.end());
        string temp(substart, subend);
        if (keep_empty || !temp.empty()) {
            result.push_back(temp);
        }
        if (subend == s.end()) {
            break;
        }
        substart = subend + delim.size();
    }
    return result;
}

int main() {
    const vector<string> words = split("So close no matter how far", " ");
    copy(words.begin(), words.end(), ostream_iterator<string>(cout, "\n"));
}

물론 Boost는 split()부분적으로 그런 식으로 작동합니다. 그리고 '공백'으로 부스트의 분할을 사용하면 실제로 모든 유형의 공백을 의미합니다 is_any_of().


마지막으로 문자열의 양쪽에서 빈 토큰을 올바르게 처리하는 솔루션
— fmuecke

53

STL에는 이러한 방법이 아직 없습니다.

그러나 멤버 strtok()를 사용하여 C의 함수를 사용 std::string::c_str()하거나 직접 작성할 수 있습니다. 빠른 Google 검색 후 찾은 코드 샘플은 다음과 같습니다 ( "STL string split" ).

void Tokenize(const string& str,
              vector<string>& tokens,
              const string& delimiters = " ")
{
    // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos     = str.find_first_of(delimiters, lastPos);

    while (string::npos != pos || string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

에서 가져온 : http://oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html

코드 샘플에 대해 궁금한 점이 있으면 의견을 남겨 주시면 설명하겠습니다.

그리고 typedef호출 된 반복자를 구현하지 않거나 과부하로 인해 <<연산자가 잘못된 코드임을 의미하지는 않습니다. 나는 C 함수를 아주 자주 사용합니다. 예를 들어, printf그리고 scanf모두가 빨라지보다 std::cin및 std::cout(크게)의fopen 구문은 바이너리 형식에 대한 더 많은 친절, 또한 작은 EXE 파일을 생성하는 경향이있다.

이 "성능 에 대한 우아함" 거래 에서 판매하지 마십시오 .


C 문자열 함수를 알고 있으며 성능 문제도 알고 있습니다 (둘 다 내 질문에 언급했습니다). 그러나이 특정 질문에 대해 우아한 C ++ 솔루션을 찾고 있습니다.
— Ashwin Nanjappa

11
@Nelson LaQuet : 추측하자 : strtok이 재진입이 아니기 때문에?
— paercebal

40
하지 않는 @Nelson 적 strtok를에 string.c_str ()를 통과! strtok은 입력 문자열을 휴지통에 버리고 (각 foudn 구분자를 대체하기 위해 '\ 0'문자를 삽입) c_str ()은 수정할 수없는 문자열을 반환합니다.
— Evan Teran

3
@ 넬슨 : 마지막 배열에서 배열의 크기는 str.size () + 1이어야합니다. 그러나 나는 "미학적"이유로 C 함수를 피하는 것이 어리 석다는 이론에 동의합니다.
— j_random_hacker

2
@ paulm : 아니요, C ++ 스트림의 속도 저하는 패싯으로 인해 발생합니다. 동기화가 비활성화되어 있고 동기화 할 수없는 문자열 스트림에서도 stdio.h 기능보다 여전히 느립니다.
— 벤 Voigt

42

다음은 스플릿 기능입니다.

  • 일반적이다
  • 표준 C ++ 사용 (부스트 없음)
  • 여러 구분 기호를 허용합니다
  • 빈 토큰을 무시합니다 (쉽게 변경할 수 있음)

    template<typename T>
    vector<T> 
    split(const T & str, const T & delimiters) {
        vector<T> v;
        typename T::size_type start = 0;
        auto pos = str.find_first_of(delimiters, start);
        while(pos != T::npos) {
            if(pos != start) // ignore empty tokens
                v.emplace_back(str, start, pos - start);
            start = pos + 1;
            pos = str.find_first_of(delimiters, start);
        }
        if(start < str.length()) // ignore trailing delimiter
            v.emplace_back(str, start, str.length() - start); // add what's left of the string
        return v;
    }

사용법 예 :

    vector<string> v = split<string>("Hello, there; World", ";,");
    vector<wstring> v = split<wstring>(L"Hello, there; World", L";,");

당신은 사용 목록에 추가하는 것을 잊었다 "매우 비효율적"
— 젠더 튤립에게

1
@ XanderTulip, 더 건설적이고 어떻게 또는 왜 설명 할 수 있습니까?
— Marco M.

3
@ XanderTulip : 값으로 벡터를 반환한다고 가정합니다. 반품 가치 최적화 (RVO, google it)가이를 처리해야합니다. 또한 C ++ 11에서는 이동 참조로 돌아갈 수 있습니다.
— Joseph Garvin

3
이것은 실제로 더 최적화 될 수 있습니다 : .push_back (str.substr (...)) 대신 .emplace_back (str, start, pos-start)를 사용할 수 있습니다. 이렇게하면 문자열 객체가 컨테이너에 구성되므로 .substr 함수로 수행되는 이동 작업 + 다른 shenanigan을 피할 수 있습니다.
— Mihai Bişog

@zoopp 예. 좋은 생각. VS10은 이것을 쓸 때 emplace_back을 지원하지 않았습니다. 답변을 업데이트하겠습니다. 감사합니다
— Marco M.

36

이 문제에 대한 2 줄 해결책이 있습니다.

char sep = ' ';
std::string s="1 This is an example";

for(size_t p=0, q=0; p!=s.npos; p=q)
  std::cout << s.substr(p+(p!=0), (q=s.find(sep, p+1))-p-(p!=0)) << std::endl;

그런 다음 인쇄하는 대신 벡터에 넣을 수 있습니다.


35

또 다른 유연하고 빠른 방법

template<typename Operator>
void tokenize(Operator& op, const char* input, const char* delimiters) {
  const char* s = input;
  const char* e = s;
  while (*e != 0) {
    e = s;
    while (*e != 0 && strchr(delimiters, *e) == 0) ++e;
    if (e - s > 0) {
      op(s, e - s);
    }
    s = e + 1;
  }
}

문자열 벡터와 함께 사용하려면 (편집 : 누군가가 STL 클래스를 상속하지 않기로 지적했기 때문에 ... hrmf;)) :

template<class ContainerType>
class Appender {
public:
  Appender(ContainerType& container) : container_(container) {;}
  void operator() (const char* s, unsigned length) { 
    container_.push_back(std::string(s,length));
  }
private:
  ContainerType& container_;
};

std::vector<std::string> strVector;
Appender v(strVector);
tokenize(v, "A number of words to be tokenized", " \t");

그게 다야! 그리고 그것은 단어를 세는 방법과 같이 토크 나이저를 사용하는 한 가지 방법입니다.

class WordCounter {
public:
  WordCounter() : noOfWords(0) {}
  void operator() (const char*, unsigned) {
    ++noOfWords;
  }
  unsigned noOfWords;
};

WordCounter wc;
tokenize(wc, "A number of words to be counted", " \t"); 
ASSERT( wc.noOfWords == 7 );

상상력에 의해 제한;)



32

표준 정규식 라이브러리 만 사용하는 간단한 솔루션이 있습니다.

#include <regex>
#include <string>
#include <vector>

std::vector<string> Tokenize( const string str, const std::regex regex )
{
    using namespace std;

    std::vector<string> result;

    sregex_token_iterator it( str.begin(), str.end(), regex, -1 );
    sregex_token_iterator reg_end;

    for ( ; it != reg_end; ++it ) {
        if ( !it->str().empty() ) //token could be empty:check
            result.emplace_back( it->str() );
    }

    return result;
}

정규식 인수를 사용하면 여러 인수 (공백, 쉼표 등)를 확인할 수 있습니다.

나는 보통 공백과 쉼표로 나누기를 확인 하므로이 기본 기능도 있습니다.

std::vector<string> TokenizeDefault( const string str )
{
    using namespace std;

    regex re( "[\\s,]+" );

    return Tokenize( str, re );
}

"[\\s,]+"공간 (검사 용 \\s) 및 쉼표 (, ).

참고 분할하려는 경우 wstring대신 string,

  • 모두 std::regex로 변경std::wregex
  • 모두 sregex_token_iterator로 변경wsregex_token_iterator

컴파일러에 따라 문자열 인수를 참조로 사용할 수도 있습니다.


이것은 내가 가장 좋아하는 대답이지만 GCC 4.8에서는 std :: regex가 깨졌습니다. 그들은 GCC 4.9에서 올바르게 구현했다고 말했다. 나는 아직도 당신이 내 +1주고있다
— mchiasson

1
이것은 당신이 말한대로 참조로 반환 된 벡터와 참조로 전달 된 인수 "str"및 "regex"도 약간 변경하여 가장 좋아합니다. 고마워.
— QuantumKarl

1
원시 문자열은 정규식 패턴을 처리하는 동안 매우 유용합니다. 그렇게하면 이스케이프 시퀀스를 사용할 필요가 없습니다 ... 그냥 사용할 수 있습니다 R"([\s,]+)".
— Sam

26

사용 std::stringstream하면서 완벽하게 작동하고 원하는 것을 정확하게 수행하십시오. 그냥하지만 일을하는 다른 방법을 찾고 있다면, 당신은 사용할 수 있습니다 std::find()/ std::find_first_of()와 std::string::substr().

예를 들면 다음과 같습니다.

#include <iostream>
#include <string>

int main()
{
    std::string s("Somewhere down the road");
    std::string::size_type prev_pos = 0, pos = 0;

    while( (pos = s.find(' ', pos)) != std::string::npos )
    {
        std::string substring( s.substr(prev_pos, pos-prev_pos) );

        std::cout << substring << '\n';

        prev_pos = ++pos;
    }

    std::string substring( s.substr(prev_pos, pos-prev_pos) ); // Last word
    std::cout << substring << '\n';

    return 0;
}

이것은 단일 문자 분리 문자에서만 작동합니다. 간단한 변경으로 여러 문자로 작업 할 수 있습니다.prev_pos = pos += delimiter.length();
— David Doria

25

boost를 사용하고 싶지만 이전에 제안 된 대부분의 솔루션에서와 같이 단일 문자 대신 전체 문자열을 구분 기호로 사용하려는 경우을 사용할 수 있습니다 boost_split_iterator.

편리한 템플릿을 포함한 예제 코드 :

#include <iostream>
#include <vector>
#include <boost/algorithm/string.hpp>

template<typename _OutputIterator>
inline void split(
    const std::string& str, 
    const std::string& delim, 
    _OutputIterator result)
{
    using namespace boost::algorithm;
    typedef split_iterator<std::string::const_iterator> It;

    for(It iter=make_split_iterator(str, first_finder(delim, is_equal()));
            iter!=It();
            ++iter)
    {
        *(result++) = boost::copy_range<std::string>(*iter);
    }
}

int main(int argc, char* argv[])
{
    using namespace std;

    vector<string> splitted;
    split("HelloFOOworldFOO!", "FOO", back_inserter(splitted));

    // or directly to console, for example
    split("HelloFOOworldFOO!", "FOO", ostream_iterator<string>(cout, "\n"));
    return 0;
}

20

표준 정규식 라이브러리 만 사용하는 정규식 솔루션입니다. (나는 약간 녹슨, 그래서 몇 가지 구문 오류가있을 수 있지만 이것은 적어도 일반적인 아이디어입니다)

#include <regex.h>
#include <string.h>
#include <vector.h>

using namespace std;

vector<string> split(string s){
    regex r ("\\w+"); //regex matches whole words, (greedy, so no fragment words)
    regex_iterator<string::iterator> rit ( s.begin(), s.end(), r );
    regex_iterator<string::iterator> rend; //iterators to iterate thru words
    vector<string> result<regex_iterator>(rit, rend);
    return result;  //iterates through the matches to fill the vector
}

아마도 더 나은 정규식 접근 방식을 사용한 비슷한 반응 : here 및 here .
— nobar

20

라는 함수가 strtok있습니다.

#include<string>
using namespace std;

vector<string> split(char* str,const char* delim)
{
    char* saveptr;
    char* token = strtok_r(str,delim,&saveptr);

    vector<string> result;

    while(token != NULL)
    {
        result.push_back(token);
        token = strtok_r(NULL,delim,&saveptr);
    }
    return result;
}

3
strtokC ++이 아닌 C 표준 라이브러리에서 가져온 것입니다. 멀티 스레드 프로그램에서 사용하는 것은 안전하지 않습니다. 입력 문자열을 수정합니다.
— 케빈 판코

13
첫 번째 호출의 char 포인터를 정적 변수에 저장하므로 NULL이 전달 될 때 후속 호출에서 어떤 포인터를 사용해야하는지 기억합니다. strtok다른 스레드가 여전히 처리 중일 때 두 번째 스레드가 호출 되면이 문자 포인터를 덮어 쓰고 두 스레드 모두 잘못된 결과를 갖습니다. mkssoftware.com/docs/man3/strtok.3.asp
— Kevin Panko

1
strtok이 안전하지 않고 C에서도 strtok_r을 사용하는 것이 좋습니다
— systemsfault

4
액세스 가능한 코드 섹션에있는 경우 strtok_r을 사용할 수 있습니다. 이것은 "라인 노이즈"가 아닌 위의 모든 것의 유일한 솔루션이며 c ++에서 정확히 무엇이 잘못되었는지에 대한 증거입니다.
— Erik Aronesty

C ++ ks 크의 스레드 안전성에 대한 반대 의견이 없도록 업데이트되었습니다.
— Erik Aronesty

17

이제 stringstream은 당신이 공백이 아닌 문자로 문자열을 구문 분석해야하는 경우 편리 할 수 있습니다 :

string s = "Name:JAck; Spouse:Susan; ...";
string dummy, name, spouse;

istringstream iss(s);
getline(iss, dummy, ':');
getline(iss, name, ';');
getline(iss, dummy, ':');
getline(iss, spouse, ';')

14

지금까지 Boost 에서 하나를 사용 했지만 그것에 의존하지 않는 것이 필요했기 때문에 다음과 같이했습니다.

static void Split(std::vector<std::string>& lst, const std::string& input, const std::string& separators, bool remove_empty = true)
{
    std::ostringstream word;
    for (size_t n = 0; n < input.size(); ++n)
    {
        if (std::string::npos == separators.find(input[n]))
            word << input[n];
        else
        {
            if (!word.str().empty() || !remove_empty)
                lst.push_back(word.str());
            word.str("");
        }
    }
    if (!word.str().empty() || !remove_empty)
        lst.push_back(word.str());
}

좋은 점은 separators둘 이상의 문자를 전달할 수 있다는 것입니다.


13

strtok을 사용하여 내 자신을 굴려서 boost를 사용하여 문자열을 나눕니다. 내가 찾은 가장 좋은 방법은 C ++ String Toolkit Library 입니다. 매우 유연하고 빠릅니다.

#include <iostream>
#include <vector>
#include <string>
#include <strtk.hpp>

const char *whitespace  = " \t\r\n\f";
const char *whitespace_and_punctuation  = " \t\r\n\f;,=";

int main()
{
    {   // normal parsing of a string into a vector of strings
        std::string s("Somewhere down the road");
        std::vector<std::string> result;
        if( strtk::parse( s, whitespace, result ) )
        {
            for(size_t i = 0; i < result.size(); ++i )
                std::cout << result[i] << std::endl;
        }
    }

    {  // parsing a string into a vector of floats with other separators
        // besides spaces

        std::string s("3.0, 3.14; 4.0");
        std::vector<float> values;
        if( strtk::parse( s, whitespace_and_punctuation, values ) )
        {
            for(size_t i = 0; i < values.size(); ++i )
                std::cout << values[i] << std::endl;
        }
    }

    {  // parsing a string into specific variables

        std::string s("angle = 45; radius = 9.9");
        std::string w1, w2;
        float v1, v2;
        if( strtk::parse( s, whitespace_and_punctuation, w1, v1, w2, v2) )
        {
            std::cout << "word " << w1 << ", value " << v1 << std::endl;
            std::cout << "word " << w2 << ", value " << v2 << std::endl;
        }
    }

    return 0;
}

툴킷은이 간단한 예제보다 훨씬 융통성이 있지만 문자열을 유용한 요소로 파싱하는 유틸리티는 놀랍습니다.


13

짧고 우아한

#include <vector>
#include <string>
using namespace std;

vector<string> split(string data, string token)
{
    vector<string> output;
    size_t pos = string::npos; // size_t to avoid improbable overflow
    do
    {
        pos = data.find(token);
        output.push_back(data.substr(0, pos));
        if (string::npos != pos)
            data = data.substr(pos + token.size());
    } while (string::npos != pos);
    return output;
}

모든 문자열을 구분 기호로 사용할 수 있으며 이진 데이터와 함께 사용할 수도 있습니다 (std :: string은 null을 포함한 이진 데이터를 지원함)

사용하여 :

auto a = split("this!!is!!!example!string", "!!");

산출:

this
is
!example!string

1
구분 기호가 문자가 아닌 문자열이 될 수 있기 때문에이 솔루션이 마음에 들지만 문자열을 수정하여 원래 문자열의 사본을 작성해야합니다.
— Alessandro Teruzzi

11

문자열과 c 기반 문자열을 쉽게 분리 할 수있는 방법이 필요했기 때문에 이것을 만들었습니다. 다른 사람도 유용하게 사용할 수 있기를 바랍니다. 또한 토큰에 의존하지 않으며 필드를 구분 기호로 사용할 수 있습니다. 이는 필자가 필요한 또 다른 키입니다.

우아함을 더욱 향상시킬 수있는 개선 사항이 있다고 확신합니다.

StringSplitter.hpp :

#include <vector>
#include <iostream>
#include <string.h>

using namespace std;

class StringSplit
{
private:
    void copy_fragment(char*, char*, char*);
    void copy_fragment(char*, char*, char);
    bool match_fragment(char*, char*, int);
    int untilnextdelim(char*, char);
    int untilnextdelim(char*, char*);
    void assimilate(char*, char);
    void assimilate(char*, char*);
    bool string_contains(char*, char*);
    long calc_string_size(char*);
    void copy_string(char*, char*);

public:
    vector<char*> split_cstr(char);
    vector<char*> split_cstr(char*);
    vector<string> split_string(char);
    vector<string> split_string(char*);
    char* String;
    bool do_string;
    bool keep_empty;
    vector<char*> Container;
    vector<string> ContainerS;

    StringSplit(char * in)
    {
        String = in;
    }

    StringSplit(string in)
    {
        size_t len = calc_string_size((char*)in.c_str());
        String = new char[len + 1];
        memset(String, 0, len + 1);
        copy_string(String, (char*)in.c_str());
        do_string = true;
    }

    ~StringSplit()
    {
        for (int i = 0; i < Container.size(); i++)
        {
            if (Container[i] != NULL)
            {
                delete[] Container[i];
            }
        }
        if (do_string)
        {
            delete[] String;
        }
    }
};

StringSplitter.cpp :

#include <string.h>
#include <iostream>
#include <vector>
#include "StringSplit.hpp"

using namespace std;

void StringSplit::assimilate(char*src, char delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.push_back(x);
            }

        }
        else
        {
            delete[] temp;
        }
    }
}

void StringSplit::assimilate(char*src, char* delim)
{
    int until = untilnextdelim(src, delim);
    if (until > 0)
    {
        char * temp = new char[until + 1];
        memset(temp, 0, until + 1);
        copy_fragment(temp, src, delim);
        if (keep_empty || *temp != 0)
        {
            if (!do_string)
            {
                Container.push_back(temp);
            }
            else
            {
                string x = temp;
                ContainerS.push_back(x);
            }
        }
        else
        {
            delete[] temp;
        }
    }
}

long StringSplit::calc_string_size(char* _in)
{
    long i = 0;
    while (*_in++)
    {
        i++;
    }
    return i;
}

bool StringSplit::string_contains(char* haystack, char* needle)
{
    size_t len = calc_string_size(needle);
    size_t lenh = calc_string_size(haystack);
    while (lenh--)
    {
        if (match_fragment(haystack + lenh, needle, len))
        {
            return true;
        }
    }
    return false;
}

bool StringSplit::match_fragment(char* _src, char* cmp, int len)
{
    while (len--)
    {
        if (*(_src + len) != *(cmp + len))
        {
            return false;
        }
    }
    return true;
}

int StringSplit::untilnextdelim(char* _in, char delim)
{
    size_t len = calc_string_size(_in);
    if (*_in == delim)
    {
        _in += 1;
        return len - 1;
    }

    int c = 0;
    while (*(_in + c) != delim && c < len)
    {
        c++;
    }

    return c;
}

int StringSplit::untilnextdelim(char* _in, char* delim)
{
    int s = calc_string_size(delim);
    int c = 1 + s;

    if (!string_contains(_in, delim))
    {
        return calc_string_size(_in);
    }
    else if (match_fragment(_in, delim, s))
    {
        _in += s;
        return calc_string_size(_in);
    }

    while (!match_fragment(_in + c, delim, s))
    {
        c++;
    }

    return c;
}

void StringSplit::copy_fragment(char* dest, char* src, char delim)
{
    if (*src == delim)
    {
        src++;
    }

    int c = 0;
    while (*(src + c) != delim && *(src + c))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

void StringSplit::copy_string(char* dest, char* src)
{
    int i = 0;
    while (*(src + i))
    {
        *(dest + i) = *(src + i);
        i++;
    }
}

void StringSplit::copy_fragment(char* dest, char* src, char* delim)
{
    size_t len = calc_string_size(delim);
    size_t lens = calc_string_size(src);

    if (match_fragment(src, delim, len))
    {
        src += len;
        lens -= len;
    }

    int c = 0;
    while (!match_fragment(src + c, delim, len) && (c < lens))
    {
        *(dest + c) = *(src + c);
        c++;
    }
    *(dest + c) = 0;
}

vector<char*> StringSplit::split_cstr(char Delimiter)
{
    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char Delimiter)
{
    do_string = true;

    int i = 0;
    while (*String)
    {
        if (*String != Delimiter && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (*String == Delimiter)
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

vector<char*> StringSplit::split_cstr(char* Delimiter)
{
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while(*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String,Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return Container;
}

vector<string> StringSplit::split_string(char* Delimiter)
{
    do_string = true;
    int i = 0;
    size_t LenDelim = calc_string_size(Delimiter);

    while (*String)
    {
        if (!match_fragment(String, Delimiter, LenDelim) && i == 0)
        {
            assimilate(String, Delimiter);
        }
        if (match_fragment(String, Delimiter, LenDelim))
        {
            assimilate(String, Delimiter);
        }
        i++;
        String++;
    }

    String -= i;
    delete[] String;

    return ContainerS;
}

예 :

int main(int argc, char*argv[])
{
    StringSplit ss = "This:CUT:is:CUT:an:CUT:example:CUT:cstring";
    vector<char*> Split = ss.split_cstr(":CUT:");

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

출력합니다 :

이것은
이다 예를 들어, CString을



int main(int argc, char*argv[])
{
    StringSplit ss = "This:is:an:example:cstring";
    vector<char*> Split = ss.split_cstr(':');

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This[SPLIT]is[SPLIT]an[SPLIT]example[SPLIT]string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string("[SPLIT]");

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

int main(int argc, char*argv[])
{
    string mystring = "This|is|an|example|string";
    StringSplit ss = mystring;
    vector<string> Split = ss.split_string('|');

    for (int i = 0; i < Split.size(); i++)
    {
        cout << Split[i] << endl;
    }

    return 0;
}

빈 항목을 유지하려면 (기본적으로 비어 있음) 제외됩니다.

StringSplit ss = mystring;
ss.keep_empty = true;
vector<string> Split = ss.split_string(":DELIM:");

목표는 문자열을 쉽게 분할하는 C #의 Split () 메서드와 비슷하게 만드는 것입니다.

String[] Split = 
    "Hey:cut:what's:cut:your:cut:name?".Split(new[]{":cut:"}, StringSplitOptions.None);

foreach(String X in Split)
{
    Console.Write(X);
}

다른 사람이 내가하는 것처럼 유용한 것을 찾을 수 있기를 바랍니다.


10

이건 어때?

#include <string>
#include <vector>

using namespace std;

vector<string> split(string str, const char delim) {
    vector<string> v;
    string tmp;

    for(string::const_iterator i; i = str.begin(); i <= str.end(); ++i) {
        if(*i != delim && i != str.end()) {
            tmp += *i; 
        } else {
            v.push_back(tmp);
            tmp = ""; 
        }   
    }   

    return v;
}

단일 구분 문자로만 분할하려는 경우이 방법이 가장 적합합니다. 원래의 질문은 공백으로 분할되기를 원했는데 이는 하나 이상의 연속 공백 또는 탭의 조합을 의미합니다. 당신은 실제로 대답했다 stackoverflow.com/questions/53849
— Oktalist

10

이 대답은 문자열을 가져 와서 문자열 벡터로 만듭니다. 부스트 라이브러리를 사용합니다.

#include <boost/algorithm/string.hpp>
std::vector<std::string> strs;
boost::split(strs, "string to split", boost::is_any_of("\t "));

9

다른 방법이 있습니다 ..

void split_string(string text,vector<string>& words)
{
  int i=0;
  char ch;
  string word;

  while(ch=text[i++])
  {
    if (isspace(ch))
    {
      if (!word.empty())
      {
        words.push_back(word);
      }
      word = "";
    }
    else
    {
      word += ch;
    }
  }
  if (!word.empty())
  {
    words.push_back(word);
  }
}

9

나는이 기준에 부스트 / 정규식 방법을 사용하고 싶습니다. 분할 기준을 지정할 때 최대한의 유연성을 제공하기 때문입니다.

#include <iostream>
#include <string>
#include <boost/regex.hpp>

int main() {
    std::string line("A:::line::to:split");
    const boost::regex re(":+"); // one or more colons

    // -1 means find inverse matches aka split
    boost::sregex_token_iterator tokens(line.begin(),line.end(),re,-1);
    boost::sregex_token_iterator end;

    for (; tokens != end; ++tokens)
        std::cout << *tokens << std::endl;
}

9

최근에 나는 낙타로 된 단어를 하위 단어로 나누었습니다. 구분 기호가 없으며 대문자 만 있습니다.

#include <string>
#include <list>
#include <locale> // std::isupper

template<class String>
const std::list<String> split_camel_case_string(const String &s)
{
    std::list<String> R;
    String w;

    for (String::const_iterator i = s.begin(); i < s.end(); ++i) {  {
        if (std::isupper(*i)) {
            if (w.length()) {
                R.push_back(w);
                w.clear();
            }
        }
        w += *i;
    }

    if (w.length())
        R.push_back(w);
    return R;
}

예를 들어 "AQueryTrades"를 "A", "Query"및 "Trades"로 분할합니다. 이 기능은 좁고 넓은 문자열에서 작동합니다. 현재 로캘을 존중하기 때문에 "RaumfahrtÜberwachungsVerordnung"을 "Raumfahrt", "Überwachungs"및 "Verordnung"으로 나눕니다.

참고 std::upper정말 기능 템플릿 인수로 전달해야한다. 다음에 분리가 더욱 나눌 수 등에서이 함수로부터 일반화 ",", ";"또는 " "도.


2
2 개정이있었습니다. 좋습니다. 내 영어가 "독일어"에 많은 것처럼 보입니다. 그러나 개정 주의자는 두 가지 사소한 버그가 수정 되었기 때문에 어쨌든 분명했기 때문에 std::isupper인수가 아닌 인수로 전달 될 수 있습니다 std::upper. 두 번째 typename앞에 String::const_iterator.
— Andreas Spindler

9
#include<iostream>
#include<string>
#include<sstream>
#include<vector>
using namespace std;

    vector<string> split(const string &s, char delim) {
        vector<string> elems;
        stringstream ss(s);
        string item;
        while (getline(ss, item, delim)) {
            elems.push_back(item);
        }
        return elems;
    }

int main() {

        vector<string> x = split("thi is an sample test",' ');
        unsigned int i;
        for(i=0;i<x.size();i++)
            cout<<i<<":"<<x[i]<<endl;
        return 0;
}

9

std::string_viewEric Niebler의 range-v3라이브러리 사용 및 :

https://wandbox.org/permlink/kW5lwRCL1pxjp2pW

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"
#include "range/v3/algorithm.hpp"

int main() {
    std::string s = "Somewhere down the range v3 library";
    ranges::for_each(s  
        |   ranges::view::split(' ')
        |   ranges::view::transform([](auto &&sub) {
                return std::string_view(&*sub.begin(), ranges::distance(sub));
            }),
        [](auto s) {std::cout << "Substring: " << s << "\n";}
    );
}

알고리즘 for대신 범위 루프 를 사용하여 ranges::for_each:

#include <iostream>
#include <string>
#include <string_view>
#include "range/v3/view.hpp"

int main()
{
    std::string str = "Somewhere down the range v3 library";
    for (auto s : str | ranges::view::split(' ')
                      | ranges::view::transform([](auto&& sub) { return std::string_view(&*sub.begin(), ranges::distance(sub)); }
                      ))
    {
        std::cout << "Substring: " << s << "\n";
    }
}

Yepp, 기반 범위가 더 좋아 보입니다-동의합니다
— Porsche9II
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.