55

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

私は試した:

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

に変わるので失敗しconst char*ますchar*

4

13 に答える 13

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

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

于 2008-11-14T06:29:38.493 に答える
20

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

char *dup = strdup(str.c_str());
token = strtok(dup, " ");
free(dup);
于 2008-11-14T06:22:42.063 に答える
20
  1. システムでブーストが利用できる場合(最近のほとんどのLinuxディストリビューションでは標準だと思います)、使用できるTokenizerクラスがあります。

  2. そうでない場合は、簡単なGoogleが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));
        }
      }
    }
    
于 2008-11-14T06:28:00.400 に答える
9

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

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

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

ライブラリ ワーキング グループは (リレハンメルでの会合で) std::vector の場合と同様に、std::string も、実際にだけでなく、形式的に、保証された連続バッファーを持つ必要があると決定しました。

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

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

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

一二三四

私たちはで終わるでしょう

1\02\0--3\0-4

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


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

于 2009-10-20T06:49:46.340 に答える
2

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

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

#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

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

于 2008-11-14T07:59:27.297 に答える
1

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

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

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

于 2008-11-14T06:23:30.347 に答える
1

「文字列」によってC++のstd::stringについて話していると仮定すると、 BoostのTokenizerパッケージを見ることができます。

于 2008-11-14T06:29:09.010 に答える
0

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

しかし、上記の両方はすでにカバーされています。
したがって、3番目のCのような代替手段として、変更のためにstd::stringをバッファーにコピーすることを提案します。

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]," ");
于 2008-11-14T10:05:48.853 に答える
0

オープン ソースを気にしない場合は、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(' ');
于 2015-06-11T13:30:52.613 に答える