- ベストアンサー
全角文字の並びを逆順にする方法とは?
- C言語で全角文字の並びを逆順にする方法について説明します。
- 全角文字の上位バイトかどうかを判定する関数を作成し、文字の並びを逆順にする関数を作成します。
- ただし、実行時エラーが発生する問題もあります。解決方法を教えていただきたいと思います。
- みんなの回答 (11)
- 専門家の回答
質問者が選んだベストアンサー
No.2 です。 No.7 のプログラムの訂正: #include <string.h> void rev_str(char *str) { char *p; char w; /*交換用ワーク*/ for (p=str; *p != '\0'; ++p) if ( is_2byte(*p) ) { w = *p; *p = p[1]; p[1] = w; ++p; /*抜けていました*/ } strrev(str); /*文字列の並びを char 単位で逆にするライブラリ関数*/ }
その他の回答 (10)
- sha-girl
- ベストアンサー率52% (430/816)
> if(is_2byte(str[i]) && j-1>=0 && i+1 <= strlen(str) ) > という行がありますが、これは > if(is_2byte(str[i]))で十分だと思うのですが、如何でしょうか? j-1 >= 1 は char buf[2] = "\xe0"; のようなデータがきた場合にメモリー破壊を回避する為の対処です。 (0xE0,0x00というデータです) if(is_2byte(str[i]))だと 以下のようなプログラムを実行するとメモリー破壊がおきます。 char buf[2] = "\xe0"; rev_str( buf ); ※i+1 <= strlen(str)の部分は冗長的でした。 もちろん正しいSJISのデータが来ることが前提であれば、このチェックは不要です。
お礼
御指摘ありがとうございます。 >j-1 >= 0 は >char buf[2] = "\xe0"; >のようなデータがきた場合にメモリー破壊を回避する為の対処です。 なるほど、そういったケースもあり得ますよね。 思いつきませんでした。
- sha-girl
- ベストアンサー率52% (430/816)
#3ではわかりやすいかと思い、元ソースをあまりいじらずサンプルコードを提示しましたが、 一時領域を使いたくないのであればこんな感じです。 void rev_str(char *str) { char tmp; char *p1 , *p2; int n , i , src_len; src_len = strlen( str ); if ( src_len == 0 ) return; /* 2バイト文字を入れ替えておく */ p1 = str; while(*p1) { if (is_2byte(*p1) ) { p1++; if (*p1 == '\0') { /* 2バイト文字が途中で切れた */ break; } tmp = *p1; *p1 = *(p1-1); *(p1-1) = tmp; p1++; } else { p1++; } } /* 逆順にする */ n = src_len / 2; p1 = str; p2 = &str[src_len - 1]; for( i = 0 ; i < n ; p1++ , p2-- , i++ ) { tmp = *p2; *p2 = *p1; *p1 = tmp; } } ※SJISコードであることからWindows環境だと思いますが、もしVisual Studioを使うなら strrevは非推奨なのでご注意ください。 http://msdn.microsoft.com/ja-jp/library/ms235404.aspx
お礼
御丁寧にありがとうございます。 是非参考にさせていただきます。 あと、No3で提示された関数において、 if(is_2byte(str[i]) && j-1>=0 && i+1 <= strlen(str) ) という行がありますが、これは if(is_2byte(str[i])) で十分だと思うのですが、如何でしょうか? (実際、様々な文字列でテストしてみて、問題ありませんでした。)
- YanenoSuzume
- ベストアンサー率44% (23/52)
No.2 です。 処理系によっては、ライブラリ関数が用意されている場合もあります。 例: LSI-C #include <jstring.h> char *jstrrev(char *s); 1バイトコードと2バイトコードが混在する 文字列s を文字単位で逆に並べかえます。 文字コードの判定では #include <jctype.h> int iskanji(char c); 全角1バイト目 int iskanji2(char c); 全角2バイト目
お礼
貴重な情報、ありがとうございます。 Borand C++ Compilerでは、jstrrev関数が存在しました。 一度この関数を試してみます。
補足
すみません。 僕の勘違いでした。 jstrrev関数は、含まれていませんでした。
- YanenoSuzume
- ベストアンサー率44% (23/52)
No.2 です。 No.4 Tacosan さん > いったんバイト単位でひっくり返しておいて, > そのあとでおもむろに後ろから見ていけば > テンポラリ領域は不要だと思う. No.5 wormhole さん > 便宜上、ASCII文字をA、漢字の1バイト目をK1, 2バイト目をK2とします。 > > A K1 K2 > > これを反転すると > > K2 K1 A …ではなくて、 A K2 K1 だと思います。 A K1 K2 ... のような文字列を A K2 K1 ... のように変形しておいてから、 後ろから出力するわけですね。 プログラムは次のようになります。 #include <string.h> void rev_str(char *str) { char *p; char w; /*交換用ワーク*/ for (p=str; *p != '\0'; ++p) if ( is_2byte(*p) ) { w = *p; *p = p[1]; p[1] = w; } strrev(str); /*文字列の並びを char 単位で逆にするライブラリ関数*/ }
お礼
御丁寧な御説明、ありがとうございます。 記述して頂いた関数、試させていただきます。
- YanenoSuzume
- ベストアンサー率44% (23/52)
No.2 です。 > strの最終文字は、2バイト文字の上位バイトではあり得ないので、 > is_2byte(temp[j])が1を返すのは、jが1以上のときだと思うのですが、 > 間違っていますでしょうか? Shift_JIS 2バイト文字のコード範囲は 上位(1バイト目) 0x81~0x9F, 0xE0~0xEF 下位(2バイト目) 0x40~0x7E, 0x80~0xFC ですので、 is_2byte(temp[0]) が 0以外(1 とは限らない)を返すことがあります。 一般に、入力データにはエラーが含まれていることも多いので、 > is_2byte(temp[j])が1を返すのは、jが1以上のときだと思うのですが、 というような思い込みでプログラムを書くのは危険で、 デバッグに苦労することになります。 「どんなデータが入力されるかわからない」と想定して 対策を講じておくことが、後々、技能の向上に役立つと思いますよ。
お礼
御回答ありがとうございます。 上位バイトの範囲と下位バイトの範囲は、一部重なっているという事に、気が付きませんでした。 御指摘されたおかげで気が付きました。
- wormhole
- ベストアンサー率28% (1626/5665)
>僕は馬鹿なので、文章を読んだだけでは、いまいちピンとこないのです。 文章を読んでわからないのであれば ノートに図をかくなりなんなりしながらわかるように 整理すればいいだけです。 便宜上、ASCII文字をA、漢字の1バイト目をK1, 2バイト目をK2とします。 A K1 K2 これを反転すると K2 K1 A これから先はご自分でご確認ください。 手順は#4の方が書かれていますよね。
- Tacosan
- ベストアンサー率23% (3656/15482)
いったんバイト単位でひっくり返しておいて, そのあとでおもむろに後ろから見ていけばテンポラリ領域は不要だと思う.
お礼
ご回答ありがとうございます。 申し訳ございませんが、可能なら具体的なコードを記述していただけませんでしょうか? 僕は馬鹿なので、文章を読んだだけでは、いまいちピンとこないのです。
- sha-girl
- ベストアンサー率52% (430/816)
>しかし、「も」以降の文字を含ませた途端、実行時エラーが発生し、プログラムがストップしてしまいます。 >また、カタカナの場合は、「メ」以降の文字を含ませた場合に、実行時エラーとなります。 SJISの「も」は0x82E0です。 下位バイトに「E0」が含まれているおり、下位バイトにあるにもかかわらずis_2byteにひっかかっています。 ※「メ」も0x8381で同じ理由です つまりSJISの場合、先頭からみていかないと2バイトコードかどうかの判定が出来ないということです。 void rev_str(char *str) { char temp[1000]; char tmp; int i, j; for(i=0, j=strlen(str)-1; i<strlen(str); i++, j--){ if(is_2byte(str[i]) && j-1>=0 && i+1 <= strlen(str) ){ temp[j]=str[i+1]; temp[j-1]=str[i]; i++;j--; } else{ temp[j]=str[i]; } } temp[strlen(str)]='\0'; strcpy(str, temp); }
お礼
鋭いご指摘、ありがとうございます。 記述していただいた関数を、一度試してみて、結果を報告します。 (今、手元に自分のPCがないので、少しお待ち下さい。)
補足
上記の関数、試させていただきました。 見事に上手く行きました。 本当に感謝します。
- YanenoSuzume
- ベストアンサー率44% (23/52)
このプログラムでは、 j=0 のときに temp[-1] を書きつぶしているのではないですか? プログラム中で確保していない領域ですので、他に影響が出ると思いますが。
お礼
ご指摘ありがとうございます。 strの最終文字は、2バイト文字の上位バイトではあり得ないので、 is_2byte(temp[j])が1を返すのは、jが1以上のときだと思うのですが、 間違っていますでしょうか?
- wormhole
- ベストアンサー率28% (1626/5665)
下位バイトの範囲は調べられましたか? 調べた上で、is_2byte()で上位バイトかどうか判定できると思われますか?
お礼
ご回答ありがとうございます。 >下位バイトの範囲は調べられましたか? 調べ回ったところ、 「SJIS で JISX0201 以外の文字は1文字2バイトで表します。 上位バイトは 0x80-0x9F, 0xE0-0xFC 下位バイトは 0x40-0x7E, 0x80-0xFC の範囲を取り、合計 11468 文字 定義できます。」 という文を見つけました。 下位バイトが 0x40-0x7E, 0x80-0xFC の範囲にあるかどうかを判定すれば、2バイト文字を検出できるかも知れませんね。 一度試してみます。
お礼
御指摘ありがとうございます。 では、修正して試させていただきます。
補足
上記の関数を試したところ、上手く行きました。 YanenoSuzumeさんには、何度も御回答いただき、大変助けられました。 本当にありがとうございました。