トークン化したい文字列があります。しかし、Cstrtok()
関数では、文字列が。である必要がありchar*
ます。どうすればこれを簡単に行うことができますか?
私は試した:
token = strtok(str.c_str(), " ");
に変わるので失敗しconst char*
ますchar*
#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;
}
または、前述のように、柔軟性を高めるためにブーストを使用します。
文字列を複製し、トークン化してから解放します。
char *dup = strdup(str.c_str());
token = strtok(dup, " ");
free(dup);
システムでブーストが利用できる場合(最近のほとんどのLinuxディストリビューションでは標準だと思います)、使用できるTokenizerクラスがあります。
そうでない場合は、簡単なGoogleがstd :: stringの手巻きのトークナイザーを表示します。これは、おそらくコピーして貼り付けるだけです。とても短いです。
そして、これらのどちらも気に入らない場合は、これが私の生活を楽にするために書いた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));
}
}
}
よりエレガントなソリューションがあります。
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
編集: 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
したがって、前述のように、ネイティブメカニズム、文字列の複製、またはサードパーティのライブラリに頼る必要があります。
言語はC、またはC++だと思います...
strtok、IIRC、区切り文字を\0に置き換えます。それがconst文字列を使用できないことです。これを「すばやく」回避するには、文字列が大きくない場合は、strdup()するだけです。文字列を変更しないでおく必要がある場合は、これが賢明です(constが示唆すること...)。
一方、与えられた引数に対して、おそらく手巻きで暴力の少ない別のトークナイザーを使用することもできます。
「文字列」によってC++のstd::stringについて話していると仮定すると、 BoostのTokenizerパッケージを見ることができます。
まず、ブーストトークナイザーを使用します。
または、データがスペースで区切られている場合は、文字列ストリームライブラリが非常に便利です。
しかし、上記の両方はすでにカバーされています。
したがって、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]," ");
オープン ソースを気にしない場合は、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(' ');