C言語関係掲示板

過去ログ

No.1264 入力した文字列がシフトJISの第1水準と第2水準のどちらにあるかを判定するプログラム

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

シフトJISの文字判定について質問
投稿者---FUJI(2004/09/09 11:29:12)


はじめまして、数ヶ月前にC言語を始めましたFUJIと申します。
言語自体初めてで悪戦苦闘しており、質問があり投稿しました。

入力した文字列がシフトJISの第1水準と第2水準のどちらにあるかを
判定するプログラムを作りたいのですが、分からずに困っています。
文字列を一文字ずつ上位バイトと下位バイトに分けて、どの水準にあるかを
判定しようと考えていますが、どのように分けて判定すればよいのでしょうか?
プログラムも組めてなく初歩的な質問で申し訳ないのですが、ご教授願います


No.16694

Re:シフトJISの文字判定について質問
投稿者---シャノン(2004/09/09 12:07:42)


>はじめまして、数ヶ月前にC言語を始めましたFUJIと申します。
>言語自体初めてで悪戦苦闘しており、質問があり投稿しました。
>
>入力した文字列がシフトJISの第1水準と第2水準のどちらにあるかを
>判定するプログラムを作りたいのですが、分からずに困っています。
>文字列を一文字ずつ上位バイトと下位バイトに分けて、どの水準にあるかを
>判定しようと考えていますが、どのように分けて判定すればよいのでしょうか?

2バイト文字を上位と下位に分ける方法ですか?
Shift_JIS なら char[] に入ってるはずですので、分ける必要はないような。

判定の仕方でしたら、まず Shift_JIS のコード表を手に入れなければならないでしょう。
こんな表見つけました。
http://www.kishugiken.co.jp/cn/code04c.html

それと、注意すべき点として、Shift_JIS コードは、文字列中のある特定のバイトだけを見て、それがどんな文字かを判定することができません。
文字列の先頭、あるいは、確実に後続バイトではないとわかっている場所から順に調べてくる必要があります。

VC++ では、独自拡張の _ismbslead という関数が、この目的で使えますが、他の処理系ではどうだか知りません。


No.16696

Re:シフトJISの文字判定について質問
投稿者---FUJI(2004/09/09 12:34:21)


>2バイト文字を上位と下位に分ける方法ですか?
>Shift_JIS なら char[] に入ってるはずですので、分ける必要はないような。
>
>判定の仕方でしたら、まず Shift_JIS のコード表を手に入れなければならないでしょう。
>こんな表見つけました。
>http://www.kishugiken.co.jp/cn/code04c.html
>
>それと、注意すべき点として、Shift_JIS コードは、文字列中のある特定のバイトだけを見て、それがどんな文字かを判定することができません。
>文字列の先頭、あるいは、確実に後続バイトではないとわかっている場所から順に調べてくる必要があります。
>
>VC++ では、独自拡張の _ismbslead という関数が、この目的で使えますが、他の処理系ではどうだか知りません。

ご返答ありがとうございました。
まだまだ質問できるレベルではなかったです。
コード表をもとに勉強していきます!


No.16698

Re:シフトJISの文字判定について質問
投稿者---Sciggepy(2004/09/09 12:38:12)


Windowsなら、IsDBCSLeadByteが使えます。シフトJIS全角文字の先頭バイトを判定するなら、
(((c)>=0x81)&&((c)<=0x9f)||((c)>=0xe0)&&((c)<=0xfc))
で多分大丈夫です。



No.16706

Re:シフトJISの文字判定について質問
投稿者---シャノン(2004/09/09 13:46:54)


>Windowsなら、IsDBCSLeadByteが使えます。シフトJIS全角文字の先頭バイトを判定するなら、
>(((c)>=0x81)&&((c)<=0x9f)||((c)>=0xe0)&&((c)<=0xfc))
>で多分大丈夫です。

それが大丈夫じゃないんです。
Shift-JIS では、先行バイトは 0x81 〜 0x9f および 0xe0 〜 0xfc に、
後続バイトは 0x40 〜 0x7e および 0x80 〜 0xfc にあります。
よく見ると、先行バイトの範囲は完全に後続バイトの範囲の中にあるのです。
そのため、ある1バイトだけを見たとき、それが先行バイトなのか後続バイトなのかを判断することは不可能で、その前に先行バイトがあるかどうかを見なければなりません。
しかし、その前にあるバイトが先行バイトかどうかもわからないので、結局、文字列の先頭から見てくるしかないのです。


No.16716

Re:シフトJISの文字判定について質問
投稿者---Sciggepy(2004/09/09 14:30:08)


シフトJISの第二バイトには、ASCIIの制御コード(必ず0x40から始まっている)は含まれないようです。
それを区切り文字として、1バイトずつ読み込んでいくとよいと思います。



No.16725

Re:シフトJISの文字判定について質問
投稿者---YuO(2004/09/09 14:49:16)


>シフトJISの第二バイトには、ASCIIの制御コード(必ず0x40から始まっている)は含まれないようです。

ASCIIの制御コードとは?C0のことでしょうか。

さらに,0x40はShift_JISの有効な第2バイトです。たとえば,ァは0x8340,
つまり第2バイトに0x40を含みます。



No.16728

Re:シフトJISの文字判定について質問
投稿者---Sciggepy(2004/09/09 16:06:02)


>>シフトJISの第二バイトには、ASCIIの制御コード(必ず0x40から始まっている)は含まれないようです。
失礼しました。
シフトJISの第二バイトには、ASCIIの制御コードは含まれないようです。(必ず0x40から始まっている)
の間違いです。

>ASCIIの制御コードとは?C0のことでしょうか。
0x20未満の値(改行コード、タブ文字など)のつもり使いました。(印字不能文字?)



No.16729

Re:シフトJISの文字判定について質問
投稿者---シャノン(2004/09/09 16:28:55)


>シフトJISの第二バイトには、ASCIIの制御コードは含まれないようです。(必ず0x40から始まっている)
>の間違いです。
>
>>ASCIIの制御コードとは?C0のことでしょうか。
>0x20未満の値(改行コード、タブ文字など)のつもり使いました。(印字不能文字?)

そうですね。付け加えて言うならば、0x7f も制御文字ですが。
で、Shift-JIS の先行/後続バイトと制御文字がダブらないことが

>>Windowsなら、IsDBCSLeadByteが使えます。シフトJIS全角文字の先頭バイトを判定するなら、
>>(((c)>=0x81)&&((c)<=0x9f)||((c)>=0xe0)&&((c)<=0xfc))
>>で多分大丈夫です。

> それが大丈夫じゃないんです。

これの解決にどう繋がってくるんでしょうか?


No.16732

Re:シフトJISの文字判定について質問
投稿者---Sciggepy(2004/09/09 17:18:44)


0x40未満の文字が第二バイトとならないということは、0x40未満の文字は、必ず第一バイトか1バイト文字ということです。
改行などの位置を配列に格納して、その位置を読み取り開始点にすれば、何回も文字列の取得を行う場合に、効率がよいかと。

#効率面での言及なので、本題から外れているようです。



No.16736

Re:シフトJISの文字判定について質問
投稿者---シャノン(2004/09/09 17:47:09)


>0x40未満の文字が第二バイトとならないということは、0x40未満の文字は、必ず第一バイトか1バイト文字ということです。
>改行などの位置を配列に格納して、その位置を読み取り開始点にすれば、何回も文字列の取得を行う場合に、効率がよいかと。
>
>#効率面での言及なので、本題から外れているようです。

であれば納得です。
俺はてっきり、どうにかすれば IsDBCSLeadByte が使い物になるようになるのかと思ってました。
結局、前から順に見てこないとピンポイントで特定できないのは同じで、その上での効率向上案だったわけですね。

#IsDBCSLeadByte が使えないわけじゃないんですけど
#MSDN に「適切な使い方」の説明があるべきのような気がします


No.16741

Re:シフトJISの文字判定について質問
投稿者---かずま(2004/09/09 21:57:06)


たいていの場合、数バイト戻るだけで、任意の位置のバイトが何かを判断
できます。常に、文字列の先頭から見ていくとか、0x40未満のバイトまで
さかのぼる必要はありません。

例えば、"文字列の先頭から見てくるしかないのです" という19文字(38バイ
ト)の文字列を16進で表すと、

 95 b6 8e 9a 97 f1 82 cc 90 e6 93 aa 82 a9 82 e7 8c a9 82 c4
 82 ad 82 e9 82 b5 82 a9 82 c8 82 a2 82 cc 82 c5 82 b7

最後の 0xb7 は、2バイト文字「す」の第2バイトですが、0xb7 だけでは
そうだと判断できません。直前の 0x82 を見てもまだ判断できません。
しかし、その前の 0xc5 を見ると、c5 82 という 2バイト文字はないので、
0x82 が第1バイトだと分かり、0xb7 が第2バイトだと判断できます。

もちろん、"ラリルレロ" (83 89 83 8a 83 8b 83 8c 83 8d) のように文字
列の先頭まで戻らないといけない場合もありますが、それは特殊な場合です。

プログラムを挙げておきます。

#define IS_FIRST(c)  (((c) ^ 0x20) - 0xa1u < 60)
#define IS_SECOND(c) ((c) >= 0x40 && (c) <= 0xfc && (c) != 0x80)

/* ktype: s[n] が 1バイト文字なら 0、2バイト文字の第1バイトなら 1、
 *        第2バイトなら 2 を返す。
 */
int ktype(const char *s, int n)
{
    const unsigned char *p = (const unsigned char *)s;  int i, t;

    for (i = n; i > 0 && IS_SECOND(p[i]) && IS_FIRST(p[i-1]); i--) ;
    for (t = 0; i < n; i++) t = t ? 0 : IS_FIRST(p[i]);
    return t ? 2 : IS_FIRST(p[i]);
}



No.16743

Re:シフトJISの文字判定について質問
投稿者---シャノン(2004/09/09 23:46:27)


><pre>
たいていの場合、数バイト戻るだけで、任意の位置のバイトが何かを判断
できます。常に、文字列の先頭から見ていくとか、0x40未満のバイトまで
さかのぼる必要はありません。

例えば、"文字列の先頭から見てくるしかないのです" という19文字(38バイ
ト)の文字列を16進で表すと、

95 b6 8e 9a 97 f1 82 cc 90 e6 93 aa 82 a9 82 e7 8c a9 82 c4
82 ad 82 e9 82 b5 82 a9 82 c8 82 a2 82 cc 82 c5 82 b7

最後の 0xb7 は、2バイト文字「す」の第2バイトですが、0xb7 だけでは
そうだと判断できません。直前の 0x82 を見てもまだ判断できません。
しかし、その前の 0xc5 を見ると、c5 82 という 2バイト文字はないので、
0x82 が第1バイトだと分かり、0xb7 が第2バイトだと判断できます。

もちろん、"ラリルレロ" (83 89 83 8a 83 8b 83 8c 83 8d) のように文字
列の先頭まで戻らないといけない場合もありますが、それは特殊な場合です。

プログラムを挙げておきます。

#define IS_FIRST(c) (((c) ^ 0x20) - 0xa1u < 60)
#define IS_SECOND(c) ((c) >= 0x40 && (c) <= 0xfc && (c) != 0x80)

/* ktype: s[n] が 1バイト文字なら 0、2バイト文字の第1バイトなら 1、
* 第2バイトなら 2 を返す。
*/
int ktype(const char *s, int n)
{
const unsigned char *p = (const unsigned char *)s; int i, t;

for (i = n; i > 0 && IS_SECOND(p[i]) && IS_FIRST(p[i-1]); i--) ;
for (t = 0; i < n; i++) t = t ? 0 : IS_FIRST(p[i]);
return t ? 2 : IS_FIRST(p[i]);
}
</pre>

あー…うん、そうかもしれませんね。
俺の最初の書き方が悪かったです。

任意の文字を判定するなら、それが効率がいいかもしれません。
でも、文字列全体を調べる場合とかだと、一文字進むたびにそれで戻るよりは、頭から見てきたほうが速いかも。

最初の質問が
> 入力した文字列がシフトJISの第1水準と第2水準のどちらにあるか
という、よくわからない質問なんですよね。

一文字なら入力されたものがどちらかを判定するだけだし、
文字列なら、その全ての文字がどちらかを判定するとかになりそうだし。
前者の場合、一文字2バイト(char[2])を文字列と呼ぶかどうか、微妙なところですが。

ただ、前者ではややこしいことは一切ありませんので、ごちゃごちゃ考える必要があるのは後者の場合かと思います。

そうすると、最初から見てきたほうがいいかも…と
自分の説を必死に擁護してみる。


No.16754

Re:シフトJISの文字判定について質問
投稿者---かずま(2004/09/10 11:52:53)


> int ktype(const char *s, int n)
> {
>     const unsigned char *p = (const unsigned char *)s;  int i, t;
> 
>     for (i = n; i > 0 && IS_SECOND(p[i]) && IS_FIRST(p[i-1]); i--) ;
>     for (t = 0; i < n; i++) t = t ? 0 : IS_FIRST(p[i]);
>     return t ? 2 : IS_FIRST(p[i]);
> }

ちょっと改良してみました。

int ktype(const void *s, int n)
{
    const unsigned char *p = s;  int i, t;

    if (!IS_SECOND(p[n])) return 0;
    for (i = n; i > 0 && IS_FIRST(p[i-1]); i--) ;
    for (t = 0; i <= n; i++) t = (t==1) ? 2 : IS_FIRST(p[i]);
    return t;
}



No.16761

Re:シフトJISの文字判定について質問
投稿者---Sciggepy(2004/09/10 18:06:33)


ところで、

#define IS_SECOND(c) ((c) >= 0x40 && (c) <= 0xfc && (c) != 0x80)

の「(c) != 0x80」は何を意味しているのですか?



No.16762

Re:シフトJISの文字判定について質問
投稿者---かずま(2004/09/10 19:04:14)


> #define IS_SECOND(c) ((c) >= 0x40 && (c) >= 0xfc && (c) != 0x80)

> の「(c) != 0x80」は何を意味しているのですか?

すみません。これは、「(c) != 0x7f」の間違いです。