ページ 11

文字化けについて

Posted: 2010年3月17日(水) 19:27
by テツ
以前お世話になりましたテツです。
今回は文字化けについてお聞きしたいと思います。

環境はBorland Bcc5.5になります。

今回プログラムを作成していて、2バイト文字が含まれる場合文字化けすることがわかりました。。。orz
ただ、文字化けしない文字もあり、どうしてなのかと思いました。

そこで、2バイト文字が含まれる文字列が渡された場合、英大文字のみを小文字にして2バイト文字はそのままに
したいと思っています。

処理的にはwhileの中で判断文を入れて2バイト文字ならtolowerをかけないで飛ばす的な感じなんでしょうか?

よろしくお願いいたします。
#include <stdio.h>
#include <ctype.h>

void StrToSmall(const char *conversionStr, char *convertedStr)
{
    while (*conversionStr) {
        *(convertedStr++) = tolower(*conversionStr++);
    }
    *convertedStr = '\0';
}

int main(void) 
{
    char    conversionStr[/url] = "HEllプログラムWORld";        // 元の文字列
    char    convertedStr[256];                                // 変換後の文字列をセット
    
    StrToSmall(conversionStr, convertedStr);
    
    printf("変換前 : %s\n", conversionStr);
    printf("変換後 : %s\n", convertedStr);
    
    return 0;
}

Re:文字化けについて

Posted: 2010年3月17日(水) 19:43
by たかぎ
二つの問題があります。

1. tolowerには0~UCHAR_MAXまたはEOFを渡さなければなりません。char型が符号付きになっていませんか?

2. シフトJISの2バイト目には、'A'~'Z'に相当するコードが含まれます。

Re:文字化けについて

Posted: 2010年3月17日(水) 19:57
by テツ
>1. tolowerには 0~UCHAR_MAXまたはEOFを渡さなければなりません。char型が符号付きになっていませんか?
これは型をunsignedにしなくてはならないということでしょうか?

検討違いだったらすみません。

Re:文字化けについて

Posted: 2010年3月17日(水) 20:26
by たかぎ
> これは型をunsignedにしなくてはならないということでしょうか?

方法はいくつかあるでしょうが、まあそういうことだと思って大丈夫です。

Re:文字化けについて

Posted: 2010年3月17日(水) 20:55
by テツ
取りあえず修正してみました。
#include <stdio.h>
#include <ctype.h>

#define IS_ZEN(c) ((c & 0xFF) >= 0x81 && (c & 0xFF) <= 0x9F || (c & 0xFF) >= 0xE0 && (c & 0xFF) <= 0xFC)

void StrToSmall(unsigned const char *conversionStr, unsigned char *convertedStr)
{
    while (*conversionStr) {
        if (IS_ZEN(*conversionStr)) {
            *(convertedStr++) = *(conversionStr++);
        }
        *(convertedStr++) = tolower(*conversionStr++);
    }
    *convertedStr = '\0';
}

int main(void) 
{
    unsigned char    conversionStr[/url] = "HEllプログラムWORld";        // 元の文字列
    unsigned char    convertedStr[256];                             // 変換後の文字列をセット
    
    StrToSmall(conversionStr, convertedStr);
    
    printf("変換前 : %s\n", conversionStr);
    printf("変換後 : %s\n", convertedStr);
    
    return 0;
}
ただこれだと変換後がhellプロバラムworldになってしまいます。

何処が悪いのでしょうか?

Re:文字化けについて

Posted: 2010年3月17日(水) 21:27
by たかぎ
下記の処理がうまくできていないようです。

2. シフトJISの[color=red>2バイト目[/color]には、'A'~'Z'に相当するコードが含まれます。

Re:文字化けについて

Posted: 2010年3月17日(水) 22:03
by テツ
う~ん。。。2バイト目には'A'~'Z'ですか。

何かもう少しヒントをいただけないでしょうか?

詰まってしまいました。。。orz

Re:文字化けについて

Posted: 2010年3月17日(水) 22:37
by ideyan
while (*conversionStr) {
    if (IS_ZEN(*conversionStr)) {
        *(convertedStr++) = *(conversionStr++);
    }
    *(convertedStr++) = tolower(*conversionStr++);
}
このコードだと、2バイト文字だったときはifの文が実行されて
1バイト目はそのままコピーされますよね?
でも、その時2バイト目はどうなるか考えてみてください。

そのままコピーされる文字もありますが
2バイト目のコードが'A'~'Z'に相当してしまった場合、
なにかまずいことが起こるような気がしませんか?

Re:文字化けについて

Posted: 2010年3月17日(水) 22:46
by テツ
>なにかまずいことが起こるような気がしませんか?
これが文字化けの原因ということでしょうか?
ということは、
if (IS_ZEN(*conversionStr)) {
        *(convertedStr++) = *(conversionStr++);
        if (0x41~0x5aの場合)
           conversionStrのアドレスを進める
    }
というような理解で大丈夫なんでしょうか?

Re:文字化けについて

Posted: 2010年3月17日(水) 23:01
by ideyan
えーと、先に一つ確認しておきたいことがあるんですが。
2バイト文字はchar配列の要素を2個使用していることは理解していますか?

Re:文字化けについて

Posted: 2010年3月17日(水) 23:07
by テツ
その辺は何となくそうなのかなぁくらいです。。。orz
う~ん。やはり理解不足みたいです。
すみません(T_T)

Re:文字化けについて

Posted: 2010年3月17日(水) 23:20
by ideyan
先ほどのプログラムは
while (*conversionStr) {
    if (IS_ZEN(*conversionStr)) { /*ここで1バイト目をみて判断*/
        /*1バイト目をコピー*/
        *(convertedStr++) = *(conversionStr++);
    }
    /* 1バイト文字の時と、2バイト文字の2バイト目はここでコピー */
    *(convertedStr++) = tolower(*conversionStr++);
}
となっています。

ですが、このままだと2バイト目にまでtolowerが実行されています。
2バイト目がA~Zのコードに該当した場合、小文字のコードに変換されて
グ→バのように文字が変わってしまうわけです。

今回、2バイト文字はそのままにするわけですから
2バイト文字だった場合→問答無用で2バイト分コピー
そうでない場合→tolowerの戻り値をコピー
と言った風にコードを書く必要があります。

Re:文字化けについて

Posted: 2010年3月18日(木) 00:01
by テツ
何度もすみません。
以下のようにしてみましたが駄目でした。
このようにしては何処が駄目なのでしょうか?
void StrToSmall(const char *conversionStr, char *convertedStr)
{
    while (*conversionStr) {
        if (IS_ZEN(*conversionStr)) {
            *(convertedStr++) = *(conversionStr++);
            *(convertedStr++) = *(conversionStr++);        // 2バイト目をコピーのつもりです
        }
        *(convertedStr++) = tolower(*conversionStr++);
    }
    *convertedStr = '\0';
}

Re:文字化けについて

Posted: 2010年3月18日(木) 00:30
by ideyan
おぉ、大分形になってきましたね^^
if (IS_ZEN(*conversionStr)) {
    /*2バイト文字用の処理*/
        *(convertedStr++) = *(conversionStr++);
        *(convertedStr++) = *(conversionStr++);
    /*ここで処理終了なのに*/
}
/*ここで余計な処理が行われる*/
/*1バイト文字用の処理*/
*(convertedStr++) = tolower(*conversionStr++);
こんな感じで、2バイト文字の時も
1バイト文字の処理がされてしまっているのが原因かと思いますよ。
なので、ifじゃなくif-elseとして処理をわけるのがいいかと思います。

Re:文字化けについて

Posted: 2010年3月18日(木) 00:34
by テツ
ideyanさん
遅くまで付き合っていただきありがとうございました。
無事希望通りの動きになりました!
本当にありがとうございます。

たかぎさんにもいろいろアドバイスをしていただきありがとうございました。
簡単ですが、この場を借りてお礼とさせていただきます。

本当にありがとうございました。