C ++에서 문자열을 어떻게 토큰 화합니까? “The quick brown fox”; String[]

Java에는 편리한 분할 방법이 있습니다.

String str = "The quick brown fox";
String[] results = str.split(" ");

C ++에서 이것을 쉽게 할 수있는 방법이 있습니까?



답변

C ++ 표준 라이브러리 알고리즘은 콘크리트 컨테이너가 아닌 반복자에 기반을두고 있습니다. 불행히도 이것이 splitC ++ 표준 라이브러리에서 Java와 유사한 기능 을 제공하는 것을 어렵게 만듭니다. 아무도 이것이 편리 할 것이라고 주장하지는 않습니다. 그러나 반환 유형은 무엇입니까? std::vector<std::basic_string<…>>? 어쩌면 우리는 (잠재적으로 중복되고 비용이 많이 드는) 할당을 수행해야합니다.

대신 C ++은 임의의 복잡한 구분 기호를 기반으로 문자열을 분할하는 다양한 방법을 제공하지만 다른 언어처럼 멋지게 캡슐화되지는 않습니다. 수많은 방법으로 전체 블로그 게시물 작성 있습니다.

가장 간단하게을 누를 std::string::find때까지를 반복 사용 std::string::npos하고를 사용하여 내용을 추출 할 수 있습니다std::string::substr .

공백 분할을위한보다 유동적 인 (관용적이지만 기본적인) 버전은 다음을 사용합니다 std::istringstream.

auto iss = std::istringstream{"The quick brown fox"};
auto str = std::string{};

while (iss >> str) {
    process(str);
}

s 사용std::istream_iterator , 문자열 스트림의 내용은 그 반복자 범위 생성자를 사용하여 벡터로 복사 할 수있다.

여러 라이브러리 (예 : Boost.Tokenizer) )는 특정 토큰 제공합니다.

고급 분할에는 정규식이 필요합니다. C ++ std::regex_token_iterator은이 목적을 위해 다음을 제공합니다 .

auto const str = "The quick brown fox"s;
auto const re = std::regex{R"(\s+)"};
auto const vec = std::vector<std::string>(
    std::sregex_token_iterator{begin(str), end(str), re, -1},
    std::sregex_token_iterator{}
);


답변

부스트 토크 나이의 클래스는 매우 간단 이런 종류의 물건을 만들 수 있습니다 :

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

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

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

C ++ 11 용으로 업데이트 :

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

using namespace std;
using namespace boost;

int main(int, char**)
{
    string text = "token, test   string";

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


답변

여기 진짜 간단한 것이 있습니다 :

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

vector<string> split(const char *str, char c = ' ')
{
    vector<string> result;

    do
    {
        const char *begin = str;

        while(*str != c && *str)
            str++;

        result.push_back(string(begin, str));
    } while (0 != *str++);

    return result;
}


답변

strtok을 사용하십시오. 내 의견으로는, strtok이 필요한 것을 제공하지 않으면 토큰 화와 관련된 클래스를 구축 할 필요가 없습니다. C와 C ++에서 15 년 이상 다양한 파싱 코드를 작성하면서 항상 strtok을 사용했습니다. 여기에 예가 있습니다

char myString[] = "The quick brown fox";
char *p = strtok(myString, " ");
while (p) {
    printf ("Token: %s\n", p);
    p = strtok(NULL, " ");
}

몇 가지주의 사항 (필요하지 않을 수도 있음). 문자열은 프로세스에서 “파기”됩니다. 즉 EOS 문자가 delimter 지점에 인라인으로 배치됩니다. 올바르게 사용하려면 문자열이 아닌 버전을 만들어야합니다. 구문 분석 중 구분 기호 목록을 변경할 수도 있습니다.

제 생각에는 위의 코드는 별도의 클래스를 작성하는 것보다 훨씬 간단하고 사용하기 쉽습니다. 나에게 이것은 언어가 제공하는 기능 중 하나이며 잘 작동합니다. 단순히 “C 기반”솔루션입니다. 적절하고 쉽고, 추가 코드를 많이 작성할 필요가 없습니다 🙂


답변

또 다른 빠른 방법은을 사용하는 것 getline입니다. 다음과 같은 것 :

stringstream ss("bla bla");
string s;

while (getline(ss, s, ' ')) {
 cout << s << endl;
}

원한다면을 split()반환하는 간단한 메소드를 만들 수 있습니다 vector<string>.


답변

스트림, 반복자 및 복사 알고리즘을 사용하여이를 직접 수행 할 수 있습니다.

#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>
#include <sstream>
#include <algorithm>

int main()
{
  std::string str = "The quick brown fox";

  // construct a stream from the string
  std::stringstream strstr(str);

  // use stream iterators to copy the stream to the vector as whitespace separated strings
  std::istream_iterator<std::string> it(strstr);
  std::istream_iterator<std::string> end;
  std::vector<std::string> results(it, end);

  // send the vector to stdout.
  std::ostream_iterator<std::string> oit(std::cout);
  std::copy(results.begin(), results.end(), oit);
}


답변

기분 나쁘게의 사람들은,하지만 같은 간단한 문제에 대한, 당신은 물건 만들기없는 방법은 너무 복잡. Boost 를 사용해야하는 많은 이유가 있습니다 . 그러나이 간단한 것에 대해서는 20 # 썰매로 비행을하는 것과 같습니다.

void
split( vector<string> & theStringVector,  /* Altered/returned value */
       const  string  & theString,
       const  string  & theDelimiter)
{
    UASSERT( theDelimiter.size(), >, 0); // My own ASSERT macro.

    size_t  start = 0, end = 0;

    while ( end != string::npos)
    {
        end = theString.find( theDelimiter, start);

        // If at end, use length=maxLength.  Else use length=end-start.
        theStringVector.push_back( theString.substr( start,
                       (end == string::npos) ? string::npos : end - start));

        // If at end, use start=maxSize.  Else use start=end+delimiter.
        start = (   ( end > (string::npos - theDelimiter.size()) )
                  ?  string::npos  :  end + theDelimiter.size());
    }
}

예를 들어 (Doug의 경우)

#define SHOW(I,X)   cout << "[" << (I) << "]\t " # X " = \"" << (X) << "\"" << endl

int
main()
{
    vector<string> v;

    split( v, "A:PEP:909:Inventory Item", ":" );

    for (unsigned int i = 0;  i < v.size();   i++)
        SHOW( i, v[i] );
}

그렇습니다. split ()이 새로운 벡터를 전달하지 않고 새로운 벡터를 반환하도록 할 수 있습니다. 그러나 내가하고있는 일에 따라 항상 새로운 객체를 만드는 것보다 기존 객체를 재사용하는 것이 좋습니다. (사이에 벡터를 비우는 것을 잊지 않는 한!)

참조 : http://www.cplusplus.com/reference/string/string/ .

(원래 Doug의 질문에 대한 응답을 작성했습니다 .C ++ Strings Modifying and Extracting based on Separators (closed) . 그러나 Martin York는 포인터를 사용하여 해당 질문을 마무리 했으므로 코드를 일반화합니다.)