掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.30308

文字列の最後から文字列の比較
投稿者---べた(2007/06/14 18:06:32)


文字列の比較を行っています。
文字列の最後から指定数の文字列を取得し、比較をしています。

処理は、比較元の文字数分、比較先の文字列の最後から取り出して
比較するというものです。
比較元、比較先とも1バイト文字、2バイト文字混在です。

比較元は、
例えば

オフィス
グループ
とか

比較先は、
例えば
xxx部
xxx事務所
xxx支店
xxxオフィス
xxxブロック

xxx
とか


一応、動いていますが、ここがおかしいとか、こうした方がよいとか、
他に、良い方法はないでしょうか。

環境は、
Solaris8
gcc


#include <stdio.h>

int chk(char *,char *);

main()
{
    char str1[]={"オフィス"};
    char str2[7][50]={"xxx部",
                      "xxx事務所",
                      "xxx支店",
                      "xxxオフィス",
                      "xxxブロック",
                      "部",
                      "xxx",
                     };
    int i;

    for (i=0; i<7; i++) {
        if (chk(str1,str2[i]) == 0) {
            printf("%s -> OK\n",str2[i]);
        } else {
            printf("%s -> NG\n",str2[i]);
        }
    }
}

int chk(char *str1,char *str2)
{
    char *a;
    int len;

    len = strlen(str1);

    if (strlen(str1) >= strlen(str2)) {
        if (strcmp(str1,str2) == 0) {
            return(0);
        } else {
            return(1);
        }
    } else {
        a = str2 + (strlen(str2) - len);
        *(a + len) = '\0';

        if (strcmp(str1,a) == 0) {
            return(0);
        } else {
            return(1);
        }
    }

    return(0);
}




この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:文字列の最後から文字列の比較 30309 acid 2007/06/14 19:01:57
<子記事> Re:文字列の最後から文字列の比較 30310 Hermit 2007/06/14 19:21:03
<子記事> Re:文字列の最後から文字列の比較 30312 yoh2 2007/06/15 00:16:56
<子記事> Re:文字列の最後から文字列の比較 30313 yoh2 2007/06/15 01:20:30


No.30309

Re:文字列の最後から文字列の比較
投稿者---acid(2007/06/14 19:01:57)


<string.h>がインクルードされてないけど、gccって警告出ないんだっけ。


必ず最後が検索元文字で終わる、って認識でいいんですよね。
strcmpを使わないでchar一文字ずつ比較するバージョン。

int chk2(char *str1, char *str2)
{
    int  len1, len2, i;

    len1 = strlen(str1);
    len2 = strlen(str2);

    for (i = 0; i < len1 && i < len2; i++) {
    
        if (str1[len1 - (i + 1)] != str2[len2 - (i + 1)]) {
            return -1;
        }
    }
    return 0;
}



この投稿にコメントする

削除パスワード

No.30311

Re:文字列の最後から文字列の比較
投稿者---Hermit(2007/06/14 20:04:58)


><string.h>がインクルードされてないけど、gccって警告出ないんだっけ。
-Wall とか付ければ警告が出ますよ。
(ワーニングのオプションは色々あったはずだけど、それしか覚えてない)



この投稿にコメントする

削除パスワード

No.30310

Re:文字列の最後から文字列の比較
投稿者---Hermit(2007/06/14 19:21:03)


ほとんどライブラリ関数に頼ったバージョン(^^;
int chk(char *str1,char *str2) {
    size_t len1 = strlen(str1), len2 = strlen(str2);
    return len1>len2 ? 1: memcmp(str1,str2+len2-len1,len1);
}



この投稿にコメントする

削除パスワード

No.30312

Re:文字列の最後から文字列の比較
投稿者---yoh2(2007/06/15 00:16:56)


>比較元、比較先とも1バイト文字、2バイト文字混在です。

Solaris8の日本語文字コードって何でしたっけ?

EUC-JP (JIS X 0213非対応) かUTF-8なら、文字コードに関連する問題はなさそう
ですが、EUC-JISX0213やShift_JISの場合、比較元の一文字が比較先の文字の一部分に
一致してしまうという問題が起こり得ます。
例1 (EUC-JISX0213 -- 2バイト文字どころか3バイト文字が出現していますが)
比較元: 「!」 [a1 aa]
比較先: 「么」 [8e a1 aa](表示されるかな? 「公」の右の払いがない文字。麻雀の役「タンヤオ」の「ヤオ」)

例2 (Shift_JIS)
比較元: 「\」 [5c]
比較先: 「表」 [95 5c]

また、これはないと思いますが、ISO-2022-JPの場合、エスケープシーケンスが邪魔して、
比較元と比較先で文字セット切り替えの位置が一致しない限り全く正しく動作しません。


この投稿にコメントする

削除パスワード

No.30316

Re:文字列の最後から文字列の比較
投稿者---YuO(2007/06/15 12:14:54)


以下,IANA登録名として名前を使っています。
>EUC-JP (JIS X 0213非対応) かUTF-8なら、文字コードに関連する問題はなさそう
>ですが、EUC-JISX0213やShift_JISの場合、比較元の一文字が比較先の文字の一部分に
>一致してしまうという問題が起こり得ます。

EUC-JPでも問題は起きますよ。
EUC-JPはG3にJIS X 0212を指示し,G3はSS3を前置してGRに呼び出して使うためです。

たとえば,「貎」 (U+8C8E, JIS X 0208 76区31点)は「」 (U+9DD7, JIS X 0212 76区31点)を ヒットさせてしまう可能性があります。


> 比較先: 「」 [8e a1 aa](表示されるかな? 「公」の右の払いがない文字。麻雀の役「タンヤオ」の「ヤオ」)

X-EUC-JISX0213でもX-EUC-JIS-2004でもG2にはJIS X 0201カタカナが呼び出されています。
よって,8E A1はHALFWIDTH IDEOGRAPHIC FULL STOPです。
ちなみに,EUC-JPにおいて8F A1 AAは未定義の文字になるはずです。


この投稿にコメントする

削除パスワード

No.30318

Re:文字列の最後から文字列の比較
投稿者---yoh2(2007/06/16 10:31:51)


いろいろと勘違いしていたみたいです。突っ込みありがとうございます。

>EUC-JPでも問題は起きますよ。
>EUC-JPはG3にJIS X 0212を指示し,G3はSS3を前置してGRに呼び出して使うためです。
>
>たとえば,「貎」 (U+8C8E, JIS X 0208 76区31点)は「」 (U+9DD7, JIS X 0212 76区31点)を ヒットさせてしまう可能性があります。

前の投稿で言いたかったことはまさにこれなのですが、EUC-JPにはJIS X 0213だけでは
なく、JIS X 0212の文字も含まれていない(SS3の前置がない→すべての文字は
1〜2バイトで表現される)と思い込んでいました。

>> 比較先: 「」 [8e a1 aa](表示されるかな? 「公」の右の払いがない文字。麻雀の役「タンヤオ」の「ヤオ」)
>
>X-EUC-JISX0213でもX-EUC-JIS-2004でもG2にはJIS X 0201カタカナが呼び出されています。

SS2(8E)はSS3(8F)の書き間違いでしたが、

>ちなみに,EUC-JPにおいて8F A1 AAは未定義の文字になるはずです。

なるほど、SS3を置いたとしてもNGだったのですね。iconvで -f euc-jisx0213を
指定すると変換できたのを見て、定義済みだと思ってしまいました。

まさに突っ込み所満載。JIS X 0213 を読み直してきます。


この投稿にコメントする

削除パスワード

No.30319

Re:文字列の最後から文字列の比較
投稿者---YuO(2007/06/16 15:16:03)


えーっと,先頭にIANA登録名として扱うと書いたのですが……。
# EUC-JISX0213ではなくX-EUC-JISX0213と書いているのは登録されていないから。


>>ちなみに,EUC-JPにおいて8F A1 AAは未定義の文字になるはずです。
>なるほど、SS3を置いたとしてもNGだったのですね。iconvで -f euc-jisx0213を
>指定すると変換できたのを見て、定義済みだと思ってしまいました。

私の書いたEUC-JPとは,IANAの登録にあるMIBenum: 18の,Extended_UNIX_Code_Packed_Format_for_Japaneseの別名としてのEUC-JPのことです。
SS3をおいてのA1 AAはJIS X 0212を参照し,そこに文字はない,ということです。

X-EUC-JISX0213やX-EUC-JIS-2004であれば話は別で,書かれているとおりU+4E48であるになります。


この投稿にコメントする

削除パスワード

No.30320

Re:文字列の最後から文字列の比較
投稿者---yoh2(2007/06/16 19:19:25)


>>>ちなみに,EUC-JPにおいて8F A1 AAは未定義の文字になるはずです。
(中略)
>X-EUC-JISX0213やX-EUC-JIS-2004であれば話は別で,書かれているとおりU+4E48であるになります。

最初の一行をX-EUC-JISX0213と読み間違えていました。重ね重ね申し訳ないです。
# 一度誤読してしまうと、なかなか補正が効かないなぁ……

以下余談。

># EUC-JISX0213ではなくX-EUC-JISX0213と書いているのは登録されていないから

これのせいで、JIS規格のいうところのEUC-JISX0213をHTMLやXML等で大手を振って使う
ことができませんね。
このスレでの例のように数値文字参照を使うか、またはユニコードに切り替えるかすれば
よい話ですが、前者は面倒臭いし、後者は、私がISO-2022好きなのでちと抵抗が。
まあ、最近は諦めてUTF-8を主に使っていますが。
# まあ、JIS規格においても諸々の事情で正式なものではなく参考に過ぎないからでしょうけど。


この投稿にコメントする

削除パスワード

No.30328

Re:文字列の最後から文字列の比較
投稿者---べた(2007/06/21 15:41:15)


yoh2さん、YuOさん
色々とありがとうございます。
大変、勉強になります。


この投稿にコメントする

削除パスワード

No.30313

Re:文字列の最後から文字列の比較
投稿者---yoh2(2007/06/15 01:20:30)


chk()の中身に関して、2箇所程気になる部分があります。
1. 無駄っぽいstrcmp()
>     if (strlen(str1) >= strlen(str2)) {
>        if (strcmp(str1,str2) == 0) {

strlen(str1) > strlen(str2)の場合は、strcmp()するまでもなく、str2が
str1で終わっていないことが分かります。
strlen(str1) == strlen(str2)の場合は、else以下の処理と共通化できます。
なので、ここは
    if (strlen(str1) <= strlen(str2)) {
        /* 元コードのelseブロックの内容 */
    }

とするのがよいのではないかと思います。

2. 比較先文字列への無意味な代入、あるいは破壊
>         a = str2 + (strlen(str2) - len);
>         *(a + len) = '\0';

aがstr2の末尾('\0'の位置)からlen文字前の位置を示しているから、 *(a + len)は
必ず'\0'になるので、代入が無駄です。
また、仮に無駄ではないとしたら、今度はstr2の内容を破壊してしまうことになり、
これまたいただけません。
"××事務所"が"事務所"で終わっていることが分かった(または、"オフィス"で終わって
いないことが分かった)まではいいけど、そのチェックが終わったら、"××事務所"でなく
なっていた、というのは嫌でしょ?
(仮引数str1、str2の型を const char * にしておくことをお勧めします)

以下は処理そのものに関して、というよりは感性とか作法とかに関する話になりますが、
気になった点をいくつか。
この辺は個人の感性が強く出てくるところですので、軽く読み流す程度でも。

3. strlen()、値をキャッシュするか、全回strlen()を呼ぶかどちからに決めた方がよさげ

せっかく最初に len = strlen(str1); としているのに、その後で何箇所か、
strlen(str1)をまた呼んでいたり、lenを使っていたりと、lenとstrlen(str1)とが
別物のような扱い。毎回strlen(str1)を呼ぶか、lenを全く使わないかに統一した方が
よいのではないかと思います。

4. 関数名が分かりづらい
関数名chkって、何をチェックして、戻り値が何を表しているのか全然伝わってきません。
static な関数ならまだしも、外部結合を持つ関数にするとしたらもっと解りやすい名前
を考えた方がよいと思います。

5. ゼロで一致、非ゼロで不一致?
chk()の戻り値の定義はどこか直感に反するような気がします。
strcmp()の戻り値に合わせたのかな、とも思いましたが、小さい、等しい、大きい、の
3種類の戻り値を表すstrcmp()と、一致、不一致、の2種類の戻り値を表すchk()とで
戻り値を合わせるというのもいかがなものかと。
もちろん、これは関数名の名付け方でまったく逆になったりもします。
例えば、関数名を unmatched_tail とすると、ゼロで一致、非ゼロで不一致の方が
しっくりしますし。


この投稿にコメントする

削除パスワード

No.30314

Re:文字列の最後から文字列の比較
投稿者---べた(2007/06/15 10:57:07)


acidさん、Hermitさん、yoh2さん
ありがとうございます。
また、いろいろとご指摘、解説ありがとうございます。

Solarisですが、日本語の文字コードですが、EUCだったと思います。
確認の仕方がわかりませんがすみませんが、どのように確認する
のですか。

実際のデータというか、比較する文字列は、「Shift_JIS」なんです。



この投稿にコメントする

削除パスワード

No.30317

Re:文字列の最後から文字列の比較
投稿者---YuO(2007/06/15 12:16:33)


>実際のデータというか、比較する文字列は、「Shift_JIS」なんです。

個人的にはiconvでも使ってUTF-16に変換してしまって,16ビット単位で比較してしまうのが簡単だと思います。
# Shift_JISであればBMPに含まれるのでサロゲートの心配がない。



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧