strcasecmpアルゴリズムに欠陥がありますか?


34

私はstrcasecmpCで関数を再実装しようとしていますが、比較プロセスで矛盾しているように見えるものに気づきました。

から man strcmp

strcmp()関数は、2つの文字列s1とs2を比較します。ロケールは考慮されません(ロケール対応の比較については、strcoll(3)を参照)。s1がそれぞれs2より小さい、一致する、またはs2より大きい場合、ゼロより小さい、等しい、またはゼロより大きい整数を返します。

から man strcasecmp

strcasecmp()関数は、文字の大文字と小文字を無視して、文字列s1とs2のバイト単位の比較を実行します。s1がそれぞれs2より小さい、一致する、またはs2より大きい場合、ゼロより小さい、等しい、またはゼロより大きい整数を返します。

int strcmp(const char *s1, const char *s2);
int strcasecmp(const char *s1, const char *s2);

この情報を考えると、次のコードの結果は理解できません。

#include <stdio.h>
#include <string.h>

int main()
{
    // ASCII values
    // 'A' = 65
    // '_' = 95
    // 'a' = 97

    printf("%i\n", strcmp("A", "_"));
    printf("%i\n", strcmp("a", "_"));
    printf("%i\n", strcasecmp("A", "_"));
    printf("%i\n", strcasecmp("a", "_"));
    return 0;
}

出力:

-1  # "A" is less than "_"
1   # "a" is more than "_"
2   # "A" is more than "_" with strcasecmp ???
2   # "a" is more than "_" with strcasecmp

の現在の文字が文字の場合、現在の文字s1が文字かどうかに関係なく、常に小文字に変換されるようs2です。

誰かがこの動作を説明できますか?1行目と3行目を同じにしないでください。

前もって感謝します!

PS:
私はgcc 9.2.0Manjaroで使用しています。
また、-fno-builtinフラグを付けてコンパイルすると、代わりに次のようになります。

-30
2
2
2

プログラムがgccの最適化された関数を使用していないためだと思いますが、問題は残っています。


2
別のテストケースをセットに追加します。printf("%i\n", strcasecmp("a", "_"));これは、おそらくと同じ結果になるはずですが、これは、これらの2つの大文字と小文字を区別しない呼び出しの1つが、大文字と小文字を区別する対応と一致しないprintf("%i\n", strcasecmp("A", "_"));ことを意味します。
anton.burger

strcasecmpあなたが参照している説明は正確ではないようです。賛成投票の回答の詳細。
Jabberwocky

9
それが意味のある唯一のものです。言う関数はA < _ && a > _ && A == a非常に多くの問題を引き起こすでしょう。
池上

余談:「Cでstrcasecmp関数を再実装しようとしている」->コードは表示されていませんが、必ず「まるで」と比較してくださいunsigned char。C17 / 18 "文字列処理<string.h>"-> "この副次句のすべての関数で、各文字は、型を持つものとして解釈されunsigned charます。これにより、char値がASCII範囲0〜127の範囲外になると、違いが生じます。
chux-モニカを

1
組み込みありとなしの出力の違いについて:結果はまったく同じで<0と> 0であり、== 0の例がないので、両方とも同じと言えます。しかし、アルゴリズムが輝いていることがわかります。戻り値の一部は、最初の等しくない文字の違いです。
busybee

回答:


31

動作は正しいです。

パーPOSIXのstr\[n\]casecmp()仕様

LC_CTYPE使用されているロケールのカテゴリがPOSIXロケールのものである場合、これらの関数は、文字列が小文字に変換されてからバイト比較が行われたかのように動作します。それ以外の場合、結果は不定です。

それはまたの一部であるNOTESの Linuxのmanページのセクション

POSIX.1-2008標準はこれらの関数について述べています:

使用されているロケールのLC_CTYPEカテゴリがPOSIXロケールのものである場合、これらの関数は、文字列が小文字に変換されてからバイト比較が実行されたかのように動作します。それ以外の場合、結果は不定です。

どうして?

@HansOlssonが彼の回答指摘したように、文字のみの大文字と小文字を区別しない比較を行い、他のすべての比較でと同じように「自然な」結果が得られるようにstrcmp()すると、並べ替えが失敗します。

場合'A' == 'a'(大文字と小文字を区別しない比較の定義)を'_' > 'A''_' < 'a'(ASCII文字セットの「自然」の結果は)両方のtrueにすることはできません。


大文字と小文字を区別せずに文字のみの比較を行っても'_' > 'A' && '_' < 'a'、最良の例とは思えない。
翼のある小惑星

1
@AsteroidsWithWingsこれらは質問で使用される文字です。そして、もし'a' == 'A' 定義によってあなたが「自然な」値との比較を行う場合には、'a''A'、との'_」あなたがすることができない間、大文字と小文字を区別しない比較を行う'A''a'平等を取得し、一貫性のあるソートの結果を得るために。
Andrew Henle

私はそれについて異議はありませんが、あなたが提供した特定の反例は関連性がないようです。
翼のある小惑星

バイナリツリーを構築するための精神的な運動を通じて@AsteroidsWithWings行く'a''A'と、'_'文字だけを変換」を提案し、木への挿入のすべての6オーダーを通過し、質問者に指定されている、「常に小文字の手紙」からの結果を比較します文字と文字を比較する場合」例えば、後者のアルゴリズムを使用して始まる'_''a'そして'A'木の両側に巻きまだ彼らは同じように定義されています。「文字と文字の比較では文字のみを小文字に変換する」アルゴリズムは壊れており、それらの3文字はそれを示しています。
Andrew Henle

さて、答えでそれを実証することをお勧めします。現時点では、それがなぜそうなると思ったはずなのかを説明せずに、'_' > 'A' そして'_' < 'a'両方は真実ではない」と指摘するところにジャンプするだけだからです。(これは、数百万の読者の1人ではなく、回答者の仕事です。)
翼を持つ小惑星

21

その他のリンク、 strcasecmpのhttp://man7.org/linux/man-pages/man3/strcasecmp.3p.htmlは、小文字への変換が正しい動作であると述べています(少なくともPOSIXロケールでは)。

その動作の理由は、strcasecmpを使用して文字列の配列をソートする場合、妥当な結果を得る必要があるためです。

それ以外の場合、たとえばqsortを使用して「A」、「C」、「_」、「b」をソートしようとすると、結果は比較の順序に依存します。


3
それ以外の場合、たとえばqsortを使用して「A」、「C」、「_」、「b」をソートしようとすると、結果は比較の順序に依存します。 いい視点ね。これが、POSIXが動作を指定している理由と考えられます。
Andrew Henle

6
より具体的には、並べ替えには完全な順序が必要ですが、質問のように比較を定義する場合はそうではありません(推移的でないため)。
デュケリング

8

s1の現在の文字が文字の場合、s2の現在の文字が文字であるかどうかに関係なく、常に小文字に変換されるようです。

それは正しいです-そしてそれはstrcasecmp()関数何をすべきかです!これは規格のPOSIX一部ではなく機能ですCが、「The Open Group Base Specifications、Issue 6」より:

POSIXロケールでは、strcasecmp()とstrncasecmp()は、文字列が小文字に変換されてからバイト比較が実行されたかのように動作します。結果は他のロケールでは規定されていません。

ちなみに、この動作は_stricmp()(Visual Studio / MSCVで使用されている)関数にも関係しています。

_stricmp関数は、通常、各文字を小文字に変換した後でstring1とstring2を比較し、それらの関係を示す値を返します。


2

以下のためのASCII 10進コードがAある65ため_である95とのためaである97ので、strcmp()それを行うと仮定何やっています。辞書式的に言えば_、それより小さくa、大きいですA

strcasecmp()* と見なさAれます。出力よりも大きいので、これも正しいです。aa_

* POSIX.1-2008標準では、これらの関数(strcasecmp()およびstrncasecmp())について述べています。

使用されているロケールのLC_CTYPEカテゴリがPOSIXロケールのものである場合、これらの関数は、文字列が小文字に変換されてからバイト比較が実行されたかのように動作します。それ以外の場合、結果は不定です。

出典:http : //man7.org/linux/man-pages/man3/strcasecmp.3.html


3
OPのポイントは、大文字と小文字を区別せA_に比較する場合よりも「大きい」ことであり、大文字と小文字を区別して比較する場合と結果が同じにならないのは不思議です。
anton.burger

6
ステートメントSince strcasecmp() `は大文字と小文字を区別しません。Aはa`であると無効な控除と見なします。大文字と小文字を区別しないルーチンは、すべての大文字を小文字であるかのように処理したり、すべての小文字を大文字であるかのように処理したり、各大文字を対応する小文字と同等として処理したり、その逆を行ったりすることができます。未加工の値を持つ非文字に変換します。この回答では、これらの可能性を優先する理由を述べていません(正しい理由は、ドキュメントで小文字を使用するように言われているためです)。
Eric Postpischil

@EricPostpischil POSIX.1-2008標準はこれらの関数(strcasecmp()およびstrncasecmp())について述べています:使用されているロケールのLC_CTYPEカテゴリがPOSIXロケールからのものである場合、これらの関数は文字列が小文字の後にバイト比較が実行されます。それ以外の場合、結果は不定です。
anastaciu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.