一定のCスタイルの文字列があるとしましょう
const char* msg = "fred,jim,345,7665";
これをトークン化して個々のフィールドを読み上げたいのですが、パフォーマンス上の理由からコピーを作成したくありません。これどうやってするの?
明らかに strtok は非定数ポインターを取り、boost::tokenizer はオプションですが、舞台裏で何をしているのかわかりません。
一定のCスタイルの文字列があるとしましょう
const char* msg = "fred,jim,345,7665";
これをトークン化して個々のフィールドを読み上げたいのですが、パフォーマンス上の理由からコピーを作成したくありません。これどうやってするの?
明らかに strtok は非定数ポインターを取り、boost::tokenizer はオプションですが、舞台裏で何をしているのかわかりません。
どのように使用するかによって異なります。次のトークンを取得してから次のトークンを取得する場合 (文字列の反復のように、実際には現在のトークンをメモリにコピーするだけで済みます。
long strtok2( char *strDest, const char *strSrc, const char cTok, long lOffset, long lMax)
{
if(lMax > 0)
{
strSrc += lOffset;
char * start = strDest;
while(--lMax && *strSrc != cTok && (*strDest++ = * strSrc++) );
*strDest = 0; //for when the token was found, not the null.
return strDest - start - 1; //the length of the token
}
return 0;
}
http://vijayinterviewquestions.blogspot.com.au/2007/07/implement-strcpy-function.htmlから単純な strcpy を入手しました
const char* msg = "fred,jim,345,7665";
char * buffer[20];
long offset = 0;
while(length = strtok2(buffer, msg, ',', offset, 20))
{
cout << buffer;
offset += (length+1);
}
まあ、もう少し詳細がないと、あなたが何を望んでいるのかを正確に知るのは難しい. 連続した区切り文字を長さゼロのトークンとして扱う必要がある区切られたアイテムを解析していると思います (これは通常、コンマ区切りの要素に対して正しいです)。また、空白行は長さゼロの単一のトークンとしてカウントされると想定しています。これは私がそれにアプローチする方法です:
const char *token_begin = msg;
int length;
for(;;)
{
length = 0;
while(!isDelimiter(token_begin[length])) //< must include \0 as delimiter
++length;
//..do something here with token. token is at: token_begin[0..length)
if ( token_begin[length] != 0 )
token_begin = &token_begin[length+1]; //skip beyond non-null delimiter
else
break; //token null terminated. exit
}
コピーされる部分文字列であっても、必然的に文字列のコピーが必要になります。
strtok_r 関数がある場合はそれを使用できますが、その作業を行うには変更可能な文字列が必要です。ただし、すべてのシステム (Windows など) がこの機能を提供しているわけではないことに注意してください。そのため、ここで実装を提供しました。次の一致のアドレスを保存するための C 文字列へのポインターです。これにより、理論的にはより再入可能 (スレッドセーフ) にすることができます。ただし、値を変更することはできます。必要に応じて、必要に応じて変更できます。たとえば、N バイトを宛先バッファーにコピーし、そのバッファーを null で終了して、ソース文字列を変更する必要がないようにすることができます。
/*
Usage:
char *tok;
char *savep;
tok = mystrtok_r (somestr, ",", &savep);
while (NULL != tok)
{
/* Do something with `tok'. */
tok = mystrtok_r (NULL, ",", &savep);
}
*/
char *
mystrtok_r (char *str, const char *delims, char **nextp)
{
if (str == NULL)
str = *nextp;
str += strspn (str, delims);
*nextp = str + strcspn (str, delims);
**nextp = 0;
if (*str == 0)
return NULL;
++*nextp;
return str;
}
これが私の提案です。私のコードは構造化されており、グローバル変数を使用していますpos
(グローバル変数が悪い習慣であることは知っていますが、アイデアを提供するだけです)。OOP が必要な場合は、データ メンバーに置き換えることができます。
int position, messageLength;
char token[MAX]; // MAX = Value greater than the maximum length
// of the tokens(e.g. 1,000);
bool hasNext()
{
return position < messageLength;
}
char* next(const char* message)
{
int i = 0;
while (position < messageLength && message[position] != ',') {
token[i++] = message[position];
position++;
}
position++; // ',' found
token[i] = '\0';
return token;
}
int main(int argc, char **argv)
{
const char* msg = "fred,jim,345,7665";
position = 0;
messageLength = strlen(msg);
while (hasNext())
cout << next(msg) << endl;
return EXIT_SUCCESS;
}
トークンをどこかに保存する場合は、いずれにせよコピーが必要であり、文字列を使用してその中に終了文字をstrtok
配置することでこれをうまく行います。null
コピーを回避するために私が目にする唯一の他のオプションは、文字列を読み取り、ステートマシンを介して文字列をスキャンし、部分的な結果をバッファに格納することでトークンを生成するレクサーですが、いずれの場合も、すべてのトークンを少なくともnullに格納する必要があります終了した文字列は、実際には何も保存していません。