startsWith(str_a, str_b)
標準Cライブラリのようなものはありますか?
nullbytesで終わる2つの文字列へのポインタを取り、最初の文字列が2番目の文字列の先頭にも完全に表示されるかどうかを教えてください。
例:
"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc" -> true
startsWith(str_a, str_b)
標準Cライブラリのようなものはありますか?
nullbytesで終わる2つの文字列へのポインタを取り、最初の文字列が2番目の文字列の先頭にも完全に表示されるかどうかを教えてください。
例:
"abc", "abcdef" -> true
"abcdef", "abc" -> false
"abd", "abdcef" -> true
"abc", "abc" -> true
回答:
どうやらこれのための標準的なC関数はありません。そう:
bool startsWith(const char *pre, const char *str)
{
size_t lenpre = strlen(pre),
lenstr = strlen(str);
return lenstr < lenpre ? false : memcmp(pre, str, lenpre) == 0;
}
上記は素晴らしく明確ですが、タイトなループで実行している場合、または非常に大きな文字列で作業している場合は、両方の文字列の全長を前もってスキャンするため、最高のパフォーマンスは得られません(strlen
)。wj32やChristophのようなソリューションは、より良いパフォーマンスを提供する可能性があります(ただし、ベクトル化に関するこのコメントは、私のCの知識を超えています)。また、を回避するFred Fooのソリューションにも注意してください(彼の言う通り、代わりにを使用する場合は不要です)。(非常に)大きな文字列またはタイトなループでの繰り返しの使用にのみ重要ですが、重要な場合は重要です。strlen
str
strncmp
memcmp
memcmp
、strncmp
より高速です。両方の文字列に少なくともlenpre
バイトがあることがわかっているため、UBはありません。strncmp
両方の文字列の各バイトでNULをチェックしますが、strlen
呼び出しでは、NULがないことがすでに保証されています。(ただし、実際の一般的な初期シーケンスよりも長い場合、pre
またはstr
それよりも長い場合でも、パフォーマンスに
memcmp
上記を使用しても、ここでの別の回答からは適切ではないため、先に進んで回答を変更しました。
strlen
、memcmp
非常に高速なハードウェア命令で実装でき、strlen
sが文字列をキャッシュに入れて、メモリの二重ヒットを回避できるためです。このようなマシンでstrncmp
は、2つstrlen
のsとmemcmp
同じように実装できますが、共通のプレフィックスが短い長い文字列では時間がかかる可能性があるため、ライブラリの作成者がこれを行うのは危険です。ここで、そのヒットは明示的であり、strlen
sはそれぞれ1回だけ実行されます(Fred Fooのstrlen
+strncmp
は3を実行します)。
このための標準関数はありませんが、定義することができます
bool prefix(const char *pre, const char *str)
{
return strncmp(pre, str, strlen(pre)) == 0;
}
C標準(7.21.4.4/2)によると、str
以下のpre
理由よりも短くなることを心配する必要はありません。
strncmp
機能は以上でないと比較n
配列から(ヌル文字に続く文字が比較されていない)文字で指さs1
によって指された配列へs2
。」
strncmp
ます。
strncmp
とstrlen
は呼ばれないものを採用するアルゴリズム。
私はおそらく一緒に行くでしょうがstrncmp()
、楽しみのために生の実装:
_Bool starts_with(const char *restrict string, const char *restrict prefix)
{
while(*prefix)
{
if(*prefix++ != *string++)
return 0;
}
return 1;
}
strncmp
コンパイラがベクトル化に本当に優れている場合を除いて、これはより遅くなる可能性があります。なぜなら、glibcの作成者は確かに:-)
strstr()
関数を使用します。 Stra == strstr(stra, strb)
最適化(v.2。-修正済み):
uint32 startsWith( const void* prefix_, const void* str_ ) {
uint8 _cp, _cs;
const uint8* _pr = (uint8*) prefix_;
const uint8* _str = (uint8*) str_;
while ( ( _cs = *_str++ ) & ( _cp = *_pr++ ) ) {
if ( _cp != _cs ) return 0;
}
return !_cp;
}
startsWith("\2", "\1")
startsWith("\1", "\1")
受け入れられたバージョンを実行し、非常に長いstrで問題が発生したため、次のロジックを追加する必要がありました。
bool longEnough(const char *str, int min_length) {
int length = 0;
while (str[length] && length < min_length)
length++;
if (length == min_length)
return true;
return false;
}
bool startsWith(const char *pre, const char *str) {
size_t lenpre = strlen(pre);
return longEnough(str, lenpre) ? strncmp(str, pre, lenpre) == 0 : false;
}
または、2つのアプローチの組み合わせ:
_Bool starts_with(const char *restrict string, const char *restrict prefix)
{
char * const restrict prefix_end = prefix + 13;
while (1)
{
if ( 0 == *prefix )
return 1;
if ( *prefix++ != *string++)
return 0;
if ( prefix_end <= prefix )
return 0 == strncmp(prefix, string, strlen(prefix));
}
}
編集: strncmpが0を返す場合、終了0または長さ(block_size)に到達したかどうかがわからないため、以下のコードは機能しません。
追加のアイデアは、ブロックごとに比較することです。ブロックが等しくない場合は、そのブロックを元の関数と比較します。
_Bool starts_with_big(const char *restrict string, const char *restrict prefix)
{
size_t block_size = 64;
while (1)
{
if ( 0 != strncmp( string, prefix, block_size ) )
return starts_with( string, prefix);
string += block_size;
prefix += block_size;
if ( block_size < 4096 )
block_size *= 2;
}
}
定数13
、64
、4096
、などの累乗はblock_size
単なる推測です。使用する入力データとハードウェアに合わせて選択する必要があります。
block_size
。インクリメントはポインタのインクリメントの後に行う必要があります。修正されました。