C言語関係掲示板

過去ログ

No824 半角全角文字入り混じり文の切り取り

[戻る] [ホームページ]
No.10420

JISコードについて
投稿者---綿下(2003/11/11 10:17:38)


突然の訪問、ご容赦ください。

早速、質問ですが。

あるデータが、半角文字全角文字入り混ざって格納されている変数より、
適当な部分で切り取って切り取ったデータ以降を別変数に格納したいのですが、格納した変数の最初の文字がJISコードダブルバイトを半分にわった文字(例えば、あいうえおだったら'い'の後ろの半角部部分と'う'の前の半角部分が重なって、よくわからない文字が取得されてしまいます。こういう場合は、1バイト戻って'い'という文字を取得するようにしたいのですが、
全角文字の半角ダブルバイトだったらというif文の聞き方が自分で探してもよくわかりません。よろしければ、御享受いただきたいのです。


No.10422

Re:JISコードについて
投稿者---たいちう(2003/11/11 13:14:43)


この質問は環境依存だと思います。

# 以下は参考までに。私の結論が誤りである可能性もあるし。
#
# 私がVCでやったときには、_ismbblead(),_ismbbtrail()等で判定しようとしましたが、
# 文字列の一番先頭から判定を始めないと完全な判定は不可能という結論に到達しました。

No.10425

Re:JISコードについて
投稿者---かずま(2003/11/11 14:01:25)


> # 以下は参考までに。私の結論が誤りである可能性もあるし。
> #
> # 私がVCでやったときには、_ismbblead(),_ismbbtrail()等で判定しようとしましたが、
> # 文字列の一番先頭から判定を始めないと完全な判定は不可能という結論に到達しました。

VC でやったということは、Shift JIS ですね。

文字列 s の n バイト目が、シングルバイト文字か、ダブルバイト文字の第1
バイトか第2バイトかは、そこからある程度さかのぼるだけで判定できます。

もちろん、"ラリルレロ" のように先頭までさかのぼらないとだめな場合も
ありますが。
#include <stdio.h>

#define isFirst(c)     (((unsigned)(c) ^ 0x20) - 0xA1 < 60)
#define isSecond(c)    ((c) >= 0x40 && (c) != 0x7F && (c) <= 0xFc)

/* type() returns 0:single byte, 1:first byte, 2:second byte */

int type(const char *s, int n)
{
    int i = n, t = 0;  unsigned char c;

    while (i > 0 && (c = s[i], isSecond(c)) && (c = s[i-1], isFirst(c)))
        --i;
    /* printf("i=%d: ", i);  // i または i-1 まで見に行った */
    do {
        t = (t == 1) ? 2 : (c = s[i], isFirst(c));
    } while (++i <= n);
    return t;
}

int main(void)
{
    char s[1024];  int i;

    while (fgets(s, sizeof s, stdin))
        for (i = 0; s[i]; i++)
            printf("s[%d] = %02x: %d\n", i, s[i] & 0xFF, type(s, i));
    return 0;
}


No.10427

Re:JISコードについて
投稿者---YuO(2003/11/11 14:48:07)


#define isSecond(c)    ((c) >= 0x40 && (c) != 0x7F && (c) <= 0xFc)


これは誤判定しませんか?
ANSI以降のCでは値保持の規則が働きますから,
(c) <= 0xFC
は,charが8bit符号付きの処理系においては常に正です。
#[-128, 127]を満たすcについて,c < 0xFC(=252)
また,
(c) >= 0x40
は,同一処理系において,unsigned charにて0x80-0xFFの値を持つ場合に常に負です。

よって,キャストが必要で
#define isSecond(c)    ((unsigned char)(c) >= 0x40 && (c) != 0x7F && (unsigned char)(c) <= 0xFC)

です。


とりあえず,nバイト目を含む文字の先頭バイトへのポインタを返す関数を書いてみました。
ISO準拠のコードを書いたはずです。
mblenを使っているので,現在のlocaleに依存します。
LC_CTYPEに適切なlocaleを指定しておく必要があります。
また,mblenを使っているので,他のmblenを使うルーチンと同時に使う場合には注意が必要です。

#include    <stdlib.h>

/*
input
    str : 有効な多バイト文字列
    pos : 調べたい文字のバイト位置
output
    NULL : 正しくない多バイト文字列 or strlen(str) <= pos
    other : posのバイトを含む文字の先頭
*/
const char * getFirstByteOf (const char * str, size_t pos)
{
    mblen(0, 0); /* 内部状態を初期化 */

    for (;;) {
        int size;

        /* 文字長を得る */
        size = mblen(str, MB_CUR_MAX);

        if (size == -1 || /* 正しい多バイト文字ではない */
            size == 0) /* ナル文字であった */
        {
            mblen(0, 0); /* 内部状態を初期化 */
            return 0;
        }

        /* posが含まれる文字であった */
        if (size > pos) {
            mblen(0, 0); /* 内部状態を初期化 */
            return str;
        }

        /* 次の文字へ進む */
        str += size;
        pos -= size;
    }
}



No.10428

Re:JISコードについて
投稿者---たいちう(2003/11/11 15:44:36)


かずま様、YuO様

最悪の場合は先頭から見なければならないとはいえ、
大抵の場合には少し遡れば判定できましたね。
私の場合は完全な判定は不要でしたので、突っ込んで
考えていませんでした。

ご教示ありがとうございました。
ソースも参考にさせていただきます。

# 元の質問者はどうなったのかな?

No.10430

Re:JISコードについて
投稿者---かずま(2003/11/11 17:18:10)


> よって,キャストが必要で
> 
> #define isSecond(c)    ((unsigned char)(c) >= 0x40 && (c) != 0x7F && (unsigned char)(c) <= 0xFC)
> 
> です。

    unsigned char c;

    (c = s[i], isSecond(c))
 
ですから、キャストは不要です。


No.10452

Re:JISコードについて
投稿者---綿下(2003/11/12 00:06:08)


どうもありがとうございました。参考にさせていただきます。


No.10423

Re:JISコードについて
投稿者---YuO(2003/11/11 13:26:37)


>あるデータが、半角文字全角文字入り混ざって格納されている変数より、
>適当な部分で切り取って切り取ったデータ以降を別変数に格納したいのですが、
>格納した変数の最初の文字がJISコードダブルバイトを半分にわった文字(例えば、あいうえおだったら'い'の後ろの半角部部分と'う'の前の半角部分が重なって、
>よくわからない文字が取得されてしまいます。

とりあえず,用語の定義をして下さい。JISコードというのは,
・JIS X 0202/7bit
・JIS X 0202/8bit
・JIS X 0221/UTF-16
・JIS X 0221/UTF-7
・JIS X 0221/UTF-8
・その他
のどれですか?
#とりあえず,符号化方式の話と考えました。

JISコードという表記が,一番解釈に困ります。
単にJIS規格にある符号化方式というなら,
Shift_JISもEUC-JPもISO-2022-JPもですから。
#see) JIS X 0208:1997


>こういう場合は、1バイト戻って'い'という文字を取得するようにしたいのですが、
>全角文字の半角ダブルバイトだったらというif文の聞き方が自分で探してもよくわかりません。
>よろしければ、御享受いただきたいのです。

とりあえず,「ダブルバイト」という話を元に組み立てると……。

大抵の符号化方式では,1バイト見ただけで複数バイトにわたる文字の先頭か否かはわかりません。
#UTF-8は可能ですが:((unsigned char)c) & 0xC0 == 0x80→後続バイト

というわけで,文字列の先頭から「文字」を意識して走査していくのがよいです。