C言語関係掲示板

過去ログ

No623 全角スペースを半角スペースに変換

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

全角スペースを半角スペースに変換
投稿者---はち(2003/05/13 14:13:45)


文字列に含まれる全角スペースを半角スペースにしているのですが、
全角スペースの判断が上手くいきません。
どこが悪いのでしょうか。

int chg_two_space(str_data)
    char    *str_data ;
{
    char   tmp_data[1024] ;
    int    i ;

    memset(tmp_data,'\0',sizeof(tmp_data)) ;

    i = 0;
    while(*(str_data+i) != '\0') {
        if ((*(str_data+i) == 0x81) && (*(str_data+i+1) == 0x40)) {
            tmp_data[i] = 0x20 ;
            i=i+2 ;
        } else {
            tmp_data[i] = *(str_data+i) ;
            i++ ;
        }
    }
    printf("tmp_data = [%s]\n",tmp_data) ;
}



No.6289

Re:全角スペースを半角スペースに変換
投稿者---しんちー(2003/05/13 14:19:18)


>文字列に含まれる全角スペースを半角スペースにしているのですが、
>全角スペースの判断が上手くいきません。
>どこが悪いのでしょうか。

いわゆる x86 系のCPUを使っている場合、リトルエンディアンなので
全角スペース (0x8140) は逆に、0x40, 0x81 の順でメモリに格納されています。

No.6291

うまくいきません
投稿者---はち(2003/05/13 14:58:38)


if ((*(str_data+j) == 0x40) && (*(str_data+j+1) == 0x81)) {
とし、
プログラムのおかしいところがあったので修正して動かして
みたのですが、正しく出力されません。
全角スペースが半角スペースに変換されません。
また、
if (*(str_data+j) == 0x40) {
で判断させると、全角スペースの次の文字が化けます。

ちなみにマシーンは、HP-UX11、UNIX-Cです。


No.6293

Re:うまくいきません
投稿者---しんちー(2003/05/13 15:17:56)


>ちなみにマシーンは、HP-UX11、UNIX-Cです。

まず、入力のエンコーディングが何かを確認して下さいね。
(0x8140 は Shift-JIS の時でしょうか)
od -cx などが使えるかと。

あと、全角スペースは半角スペース2つに変換しないと都合が悪いです。

No.6295

Re:うまくいきません
投稿者---はち(2003/05/13 15:52:13)


いろいろとありがとうございます。

>まず、入力のエンコーディングが何かを確認して下さいね。
>(0x8140 は Shift-JIS の時でしょうか)
>od -cx などが使えるかと。
>
>あと、全角スペースは半角スペース2つに変換しないと都合が悪いです。

Shift-JISです。
A B
^^
全角スペース
を、od -cx

0000000 4181 4042 0a00
A 201 @ B \n
0000005
とでました。

No.6298

Re:うまくいきません
投稿者---しんちー(2003/05/13 16:32:13)


>Shift-JISです。
>A B
> ^^
>全角スペース
>を、od -cx
>
>0000000 4181 4042 0a00
> A 201 @ B \n
>0000005
>とでました。

Shift-JIS でビッグエンディアンなんですね。
ならば判定文は 0x81,0x40 の順で書けば動作すると思います。
どうでしょう。

No.6303

Re:うまくいきません
投稿者---はち(2003/05/13 17:24:16)



>Shift-JIS でビッグエンディアンなんですね。
>ならば判定文は 0x81,0x40 の順で書けば動作すると思います。
>どうでしょう。

if ((*(str_data+j) == 0x81) && (*(str_data+j+1) == 0x40)){
とやりましたがだめでした。

根本的に考えかが抜けているのでしょうか。?



No.6307

Re:うまくいきません
投稿者---しんちー(2003/05/13 20:01:25)


>if ((*(str_data+j) == 0x81) && (*(str_data+j+1) == 0x40)){
>とやりましたがだめでした。
>
>根本的に考えかが抜けているのでしょうか。?

私も実行してないのが悪いといえば悪いんですが、
(1) 半角2文字に変換するのはやりましたよね?
(2) ある漢字の下位バイトが 0x81 で、次に @ (0x40) が来ると化けます。
テストデータはどんな文字列で、出力はどうなりますか?

と書いててわかりました。

多分コンパイル時に warning が出ると思いますが、
*(str_data+i) == 0x40

のような判定をするときに左辺が符号拡張されるのが問題だと思います。
つまり、あるバイトのデータが 0x81 だとそれは 0xffffff81 のようにみなされます。
これを回避するために、str_data[i] のように書きましょう。
すると左辺は char 型の値と判定されます。
また、0x81 は (char)0x81 と書きましょう。


No.6308

訂正
投稿者---しんちー(2003/05/13 20:07:54)


すみません、訂正です。*(str_data + i) の表現はそのままで結構です。
ちなみに、なぜ気付いたかというと、あくまでも直接は関係ないのですが
 printf("%x ", str_data[i]);
のような printf デバッグをしていて思い出しました。
int 型以外の整数を扱うときは注意しないといけません。

No.6310

さらに訂正
投稿者---しんちー(2003/05/13 21:06:12)


char にキャストするなんて不自然だよなあと考えて本見てたら、
 str_data[i] == '\x40'
のようにすればよいことがわかりました。

No.6340

ありがとう
投稿者---はち(2003/05/14 01:07:38)


いろいろとアドバイスありがとうございます。
いま、近くに環境がないので、明日確認してみます。


No.6352

全角スペースは半角スペース2つに変換しないと都合が悪いとは
投稿者---はち(2003/05/14 12:09:54)


ありがとうございます。

*(str_data+j) == 0x81
*(str_data+j) == '\x41'
のいづれでもできました。


ところで、No.6293の書き込みで、
>あと、全角スペースは半角スペース2つに変換しないと都合が悪いです。

と言われてますが、確かに、半角スペース2つにしないと都合が悪いです。
これは、どうしてなのでしょうか。

全角スペースを半角スペース2つに変換し、その後、半角スペース1つに
するには、さらに作りこみが必要ということですね。


No.6355

Re:全角スペースは半角スペース2つに変換しないと都合が悪いとは
投稿者---しんちー(2003/05/14 14:05:15)


>>あと、全角スペースは半角スペース2つに変換しないと都合が悪いです。
>と言われてますが、確かに、半角スペース2つにしないと都合が悪いです。
>これは、どうしてなのでしょうか。

配列上で文字がどのように格納されているか考えます。
本問では、全角は2バイト、半角は1バイト分の領域を使いますから、
A□B (□は全角スペース) はバイト列では
 41 81 40 42
ですが、全角スペースの1バイト目だけ変換すると
 41 20 40 42
ですから、これは A_@B (_ は半角スペース) と解釈されます。

>全角スペースを半角スペース2つに変換し、その後、半角スペース1つに
>するには、さらに作りこみが必要ということですね。

そうですね。ただ、ちょっとした工夫でできると思います。
str_data に用いる添え字の文字と、tmp_data に用いる文字を異なるものにするといいかも。

No.6362

ありがとうございます。
投稿者---はち(2003/05/14 18:44:00)


ありがとうございます。

No.6363

Re:全角スペースは半角スペース2つに変換しないと都合が悪いとは
投稿者---かずま(2003/05/14 19:02:03)


> 全角スペースを半角スペース2つに変換し、その後、半角スペース1つに
> するには、さらに作りこみが必要ということですね。

なぜ、そんな無駄なことをするのでしょうか。全角スペースが
1個見つかったら、半角スペース1個を出力するだけのことでは。
#include <string.h>

void change_space(const char *s)
{
    while (*s)
        if (memcmp(s, " ", 2))
            putchar(*s++);
        else
            putchar(' '), s += 2;
}


No.6364

Re:全角スペースは半角スペース2つに変換しないと都合が悪いとは
投稿者---かずま(2003/05/14 19:31:44)


> void change_space(const char *s)

このプログラムだと、「鈴木氏@東京都」が文字化けします。
なぜなら、「氏@」が 0x8E 0x81 0x40 なので、後ろの 2バイトが
全角スペースとマッチしてしまうからです。

訂正版です。
#define isSJIS(c)  ((c & 0xFF ^ 0x20) - 0xA1u < 60)

void change_space(const char *s)
{
    while (*s)
        if (!isSJIS(*s))
            putchar(*s++);
        else if (memcmp(s, " ", 2) == 0)
            putchar(' '), s += 2;
        else
            putchar(*s++), putchar(*s++);
}


No.6385

サンプルソースにつてい質問
投稿者---つとむ(2003/05/15 11:54:37)


サンプルのソースで教え頂きたいことがあります。

#define isSJIS(c) ((c & 0xFF ^ 0x20) - 0xA1u < 60)

else if (memcmp(s, " ", 2) == 0)
putchar(' '), s += 2;
else
putchar(*s++), putchar(*s++) ;

1行目はどのような処理を行っているのでしょうか。
分かりやすく書き直すとどうなりますか。

4行目の記述で、「,」で区切って1行で書いていますが、このような
書き方ができるのですか。
putcharの使用方法ではないでね。
putchar( ');
s += 2;
と書くのと同じですか。

6行目の記述で、putcharを2回していますが、何故なのですか。


No.6393

Re:サンプルソースにつてい質問
投稿者---かずま(2003/05/15 15:17:55)


> 1行目はどのような処理を行っているのでしょうか。
& 0xFF  -- c が符号付きだと、-128〜127 なので、0x00〜0xFF にしている。
^ 0x20  -- 0x80〜0x9F、0xE0〜0xFF を 0xA0〜0xDF の連続領域に移している。
- 0xA1u -- 0xA1 との差を求め、結果を符号なし整数にしている。
< 60    -- 0xA1〜0xDC の範囲であるかどうかを調べている。

> 分かりやすく書き直すとどうなりますか。
#define isSJIS(c) ((c & 0xFF) >= 0x81 && (c & 0xFF) <= 0x9F || \
                   (c & 0xFF) >= 0xE0 && (c & 0xFF) <= 0xFC)

> 4行目の記述で、「,」で区切って1行で書いていますが、このような
> 書き方ができるのですか。

できます。


> putcharの使用方法ではないでね。

どういう意味ですか。


> putchar( ');
> s += 2;
> と書くのと同じですか。

それでは、文が 2つになってしまいます。
{ putchar(' '); s += 2; }
と書くのと同じです。

> 6行目の記述で、putcharを2回していますが、何故なのですか。

Shift JIS の 2バイトコードだと判定できたのだから、2バイトを処理して
います。2バイト目を処理しないと、それが新たに判定の対象にされ、
「氏@」のような問題を引き起こします。

以上の説明でよろしいでしょうか?

No.6397

ありがとうございます。
投稿者---つとむ(2003/05/15 15:51:46)


詳しい解説ありがとうございます。