web-dev-qa-db-ja.com

std :: stringでstrtokを使用する

トークン化する文字列があります。しかし、C strtok()関数では、文字列がchar*である必要があります。どうすれば簡単にできますか?

私は試した:

token = strtok(str.c_str(), " "); 

これはconst char*ではなくchar*に変わるため失敗します

44
Bill
#include <iostream>
#include <string>
#include <sstream>
int main(){
    std::string myText("some-text-to-tokenize");
    std::istringstream iss(myText);
    std::string token;
    while (std::getline(iss, token, '-'))
    {
        std::cout << token << std::endl;
    }
    return 0;
}

または、前述のように、ブーストを使用して柔軟性を高めます。

61
Chris Blackwell
  1. システムで boost が使用可能な場合(最近のほとんどのLinuxディストリビューションでは標準だと思います)、使用できる Tokenizer クラスがあります。

  2. そうでない場合、簡単なGoogleが hand-rolled tokenizer をstd :: stringに返します。おそらくコピーして貼り付けることができます。とても短いです。

  3. そして、もしあなたがそれらのどちらも気に入らなければ、ここに私の人生を楽にするために書いたsplit()関数があります。区切り文字として「delim」内の任意の文字を使用して、文字列を断片に分割します。ピースは「パーツ」ベクトルに追加されます。

    void split(const string& str, const string& delim, vector<string>& parts) {
      size_t start, end = 0;
      while (end < str.size()) {
        start = end;
        while (start < str.size() && (delim.find(str[start]) != string::npos)) {
          start++;  // skip initial whitespace
        }
        end = start;
        while (end < str.size() && (delim.find(str[end]) == string::npos)) {
          end++; // skip to end of Word
        }
        if (end-start != 0) {  // just ignore zero-length strings.
          parts.Push_back(string(str, start, end-start));
        }
      }
    }
    
20
Todd Gamblin

文字列を複製し、トークン化してから解放します。

char *dup = strdup(str.c_str());
token = strtok(dup, " ");
free(dup);
15
DocMax

よりエレガントなソリューションがあります。

Std :: stringを使用すると、resize()を使用して適切なサイズのバッファーを割り当て、&s [0]を使用して内部バッファーへのポインターを取得できます。

この時点で、多くの優秀な人々が画面に飛び乗って叫びます。しかし、これは事実です。約2年前

ライブラリワーキンググループは、(Lillehammerでの会議で)std :: vectorのように、std :: stringも実際にだけでなく、正式に、連続したバッファーを保証することを決定しました。

もう1つの懸念は、strtok()が文字列のサイズを増やすことです。 MSDNドキュメントには次のように書かれています。

Strtokの各呼び出しは、その呼び出しによって返されたトークンの後にヌル文字を挿入することによりstrTokenを変更します。

しかし、これは正しくありません。実際には、関数はfirstセパレーター文字の出現を\ 0に置き換えます。文字列のサイズに変更はありません。この文字列がある場合:

1 2 3 4

で終わります

one\0two\0--three\0-four

だから私の解決策は非常に簡単です:


std::string str("some-text-to-split");
char seps[] = "-";
char *token;

token = strtok( &str[0], seps );
while( token != NULL )
{
   /* Do your thing */
   token = strtok( NULL, seps );
}

http://www.archivum.info/comp.lang.c++/2008-05/02889/does_std::string_have_something_like_CString::GetBuffer の説明を読む

6
Martin Dimitrov

言語はC、またはC++だと思います...

strtok、IIRCは、区切り文字を\ 0に置き換えます。それは、const文字列を使用できないことです。 「すばやく」回避するには、文字列が大きくない場合は、strdup()するだけです。文字列を変更しないでおく必要がある場合は賢明です(constが示唆すること...)。

その一方で、別のトークナイザーを使用することもできます。おそらく、手巻きで、与えられた引数に対して暴力的ではありません。

1
PhiLho

"string"でC++のstd :: stringについて話していると仮定すると、 BoostTokenizer パッケージを見ることができます。

1
Sherm Pendley

編集:constキャストの使用法はonly string :: c_str()によって返されるポインターに適用されたときのstrtok()の効果を示すために使用されます。

strtok()を使用しないでくださいトークン化された文字列を変更するため、Cインスタンスが文字列インスタンスに「属する」ため、定義されていない場合でも望ましくない動作を引き起こす可能性があります。

_#include <string>
#include <iostream>

int main(int ac, char **av)
{
    std::string theString("hello world");
    std::cout << theString << " - " << theString.size() << std::endl;

    //--- this cast *only* to illustrate the effect of strtok() on std::string 
    char *token = strtok(const_cast<char  *>(theString.c_str()), " ");

    std::cout << theString << " - " << theString.size() << std::endl;

    return 0;
}
_

strtok()を呼び出した後、スペースは文字列から「削除」されたか、印刷できない文字になりましたが、長さは変わりません。

_>./a.out
hello world - 11
helloworld - 11
_

したがって、前述のネイティブメカニズム、文字列の複製、またはサードパーティのライブラリを使用する必要があります。

1
philant

オープンソースを気にしない場合は、 https://github.com/EdgeCast/json_parser のサブバッファーおよびサブパーサークラスを使用できます。元の文字列はそのまま残り、割り当てもデータのコピーもありません。以下をコンパイルしていないため、エラーが発生する可能性があります。

std::string input_string("hello world");
subbuffer input(input_string);
subparser flds(input, ' ', subparser::SKIP_EMPTY);
while (!flds.empty())
{
    subbuffer fld = flds.next();
    // do something with fld
}

// or if you know it is only two fields
subbuffer fld1 = input.before(' ');
subbuffer fld2 = input.sub(fld1.length() + 1).ltrim(' ');
0
Scott Yeager

まず、ブーストトークナイザーを使用します。
また、データがスペースで区切られている場合、文字列ストリームライブラリは非常に便利です。

しかし、上記の両方はすでにカバーされています。

std::string   data("The data I want to tokenize");

// Create a buffer of the correct length:
std::vector<char>  buffer(data.size()+1);

// copy the string into the buffer
strcpy(&buffer[0],data.c_str());

// Tokenize
strtok(&buffer[0]," ");
0
Martin York

C++ 17では、_str::string_は、変更可能なバッファーへのポインターを返すdata()オーバーロードを受け取ります。そのため、strtokで文字列をハッキングなしで直接使用できます。

_#include <string>
#include <iostream>
#include <cstring>
#include <cstdlib>

int main()
{
    ::std::string text{"pop dop rop"};
    char const * const psz_delimiter{" "};
    char * psz_token{::std::strtok(text.data(), psz_delimiter)};
    while(nullptr != psz_token)
    {
        ::std::cout << psz_token << ::std::endl;
        psz_token = std::strtok(nullptr, psz_delimiter);
    }
    return EXIT_SUCCESS;
}
_

出力

ポップ
dop
rop

0
VTT