-PR-
  • 困ってます
  • 質問No.6996802
解決
済み

全角文字を含んだ文字の並びを逆順にするには?

  • 閲覧数911
  • ありがとう数10
  • 気になる数0
  • 回答数11
  • コメント数0

お礼率 78% (349/443)

こんにちは。

C言語において、以下のような2つの関数を利用して、全角文字を含んだ文字列の並びを逆順にする事を考えました。
ちなみにコンパイラは、borlandのC++コンパイラを使っています。

文字コードがShift_JISの場合、全角文字の上位バイトが0x81~0x9F、0xE0~0xFCの範囲に収まるという事を聞いたので、まず、以下のように、渡された文字が、全角文字の上位バイトかどうかを判定する関数を作りました。
____________________________________________________________
int is_2byte(unsigned char c){
return ( ( (c >= 0x81) && (c <= 0x9f) ) || ( (c >= 0xe0) && (c <= 0xfc) ) );
}
____________________________________________________________

次に、上の関数を利用し、以下のように、渡された文字列の、文字の並びを逆順にする関数を作りました。
____________________________________________________________

void rev_str(char *str)
{
char temp[1000];
char tmp;
int i, j;

for(i=strlen(str)-1, j=0; i>=0; i--, j++){
temp[j]=str[i];
if(is_2byte(temp[j])){
tmp=temp[j-1];
temp[j-1]=temp[j];
temp[j]=tmp;
}
}
temp[j]='\0';
strcpy(str, temp);
}
__________________________________________________

この関数を、あいうえお順に試して行った所、ひらがなの
「あいうえお かきくけこ さしすせそ たちつてと なにぬねの はひふへほ まみむめ」
という文字列は、逆順にする事ができたのできました。
しかし、「も」以降の文字を含ませた途端、実行時エラーが発生し、プログラムがストップしてしまいます。
また、カタカナの場合は、「メ」以降の文字を含ませた場合に、実行時エラーとなります。

何が問題なのかが全然分からないので、困っています。
何かいい方法を知っておられる方がいらっしゃれば、是非アドバイスを頂きたいと思います。
では、よろしくお願い致します。
通報する
  • 回答数11
  • 気になる
    質問をブックマークします。
    マイページでまとめて確認できます。

質問者が選んだベストアンサー

  • 回答No.9
レベル8

ベストアンサー率 44% (22/50)

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 単位で逆にするライブラリ関数*/
}
補足コメント
MetalLover

お礼率 78% (349/443)

上記の関数を試したところ、上手く行きました。
YanenoSuzumeさんには、何度も御回答いただき、大変助けられました。
本当にありがとうございました。
投稿日時 - 2011-09-12 09:23:52
お礼コメント
MetalLover

お礼率 78% (349/443)

御指摘ありがとうございます。
では、修正して試させていただきます。
投稿日時 - 2011-09-12 06:56:59
-PR-
-PR-

その他の回答 (全10件)

  • 回答No.3
レベル13

ベストアンサー率 52% (430/816)

>しかし、「も」以降の文字を含ませた途端、実行時エラーが発生し、プログラムがストップしてしまいます。 >また、カタカナの場合は、「メ」以降の文字を含ませた場合に、実行時エラーとなります。 SJISの「も」は0x82E0です。 下位バイトに「E0」が含まれているおり、下位バイトにあるにもかかわらずis_2byteにひっかかっています。 ※「メ」も0x8381で同じ理由です つまりSJISの場合、 ...続きを読む
>しかし、「も」以降の文字を含ませた途端、実行時エラーが発生し、プログラムがストップしてしまいます。
>また、カタカナの場合は、「メ」以降の文字を含ませた場合に、実行時エラーとなります。
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);
}
補足コメント
MetalLover

お礼率 78% (349/443)

上記の関数、試させていただきました。
見事に上手く行きました。
本当に感謝します。
投稿日時 - 2011-09-12 09:26:49
お礼コメント
MetalLover

お礼率 78% (349/443)

鋭いご指摘、ありがとうございます。

記述していただいた関数を、一度試してみて、結果を報告します。
(今、手元に自分のPCがないので、少しお待ち下さい。)
投稿日時 - 2011-09-11 05:11:03
  • 回答No.2
レベル8

ベストアンサー率 44% (22/50)

このプログラムでは、 j=0 のときに temp[-1] を書きつぶしているのではないですか? プログラム中で確保していない領域ですので、他に影響が出ると思いますが。 ...続きを読む
このプログラムでは、 j=0 のときに temp[-1] を書きつぶしているのではないですか?
プログラム中で確保していない領域ですので、他に影響が出ると思いますが。
お礼コメント
MetalLover

お礼率 78% (349/443)

ご指摘ありがとうございます。

strの最終文字は、2バイト文字の上位バイトではあり得ないので、
is_2byte(temp[j])が1を返すのは、jが1以上のときだと思うのですが、
間違っていますでしょうか?
投稿日時 - 2011-09-11 05:05:39
  • 回答No.1
レベル14

ベストアンサー率 29% (1088/3714)

下位バイトの範囲は調べられましたか? 調べた上で、is_2byte()で上位バイトかどうか判定できると思われますか? ...続きを読む
下位バイトの範囲は調べられましたか?
調べた上で、is_2byte()で上位バイトかどうか判定できると思われますか?
お礼コメント
MetalLover

お礼率 78% (349/443)

ご回答ありがとうございます。

>下位バイトの範囲は調べられましたか?
調べ回ったところ、

「SJIS で JISX0201 以外の文字は1文字2バイトで表します。 上位バイトは 0x80-0x9F, 0xE0-0xFC 下位バイトは 0x40-0x7E, 0x80-0xFC の範囲を取り、合計 11468 文字 定義できます。」

という文を見つけました。
下位バイトが 0x40-0x7E, 0x80-0xFC の範囲にあるかどうかを判定すれば、2バイト文字を検出できるかも知れませんね。
一度試してみます。
投稿日時 - 2011-09-11 05:00:00
  • 回答No.4
レベル14

ベストアンサー率 23% (3656/15479)

いったんバイト単位でひっくり返しておいて, そのあとでおもむろに後ろから見ていけばテンポラリ領域は不要だと思う. ...続きを読む
いったんバイト単位でひっくり返しておいて, そのあとでおもむろに後ろから見ていけばテンポラリ領域は不要だと思う.
お礼コメント
MetalLover

お礼率 78% (349/443)

ご回答ありがとうございます。

申し訳ございませんが、可能なら具体的なコードを記述していただけませんでしょうか?
僕は馬鹿なので、文章を読んだだけでは、いまいちピンとこないのです。
投稿日時 - 2011-09-11 05:09:11
  • 回答No.6
レベル8

ベストアンサー率 44% (22/50)

No.2 です。 > strの最終文字は、2バイト文字の上位バイトではあり得ないので、 > is_2byte(temp[j])が1を返すのは、jが1以上のときだと思うのですが、 > 間違っていますでしょうか? Shift_JIS 2バイト文字のコード範囲は 上位(1バイト目) 0x81~0x9F, 0xE0~0xEF 下位(2バイト目) 0x40~0x7E, 0x80~0xFC です ...続きを読む
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以上のときだと思うのですが、

というような思い込みでプログラムを書くのは危険で、
デバッグに苦労することになります。
「どんなデータが入力されるかわからない」と想定して
対策を講じておくことが、後々、技能の向上に役立つと思いますよ。
お礼コメント
MetalLover

お礼率 78% (349/443)

御回答ありがとうございます。

上位バイトの範囲と下位バイトの範囲は、一部重なっているという事に、気が付きませんでした。
御指摘されたおかげで気が付きました。
投稿日時 - 2011-09-12 09:25:02
  • 回答No.10
レベル13

ベストアンサー率 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バイ ...続きを読む
#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
お礼コメント
MetalLover

お礼率 78% (349/443)

御丁寧にありがとうございます。
是非参考にさせていただきます。

あと、No3で提示された関数において、
if(is_2byte(str[i]) && j-1>=0 && i+1 <= strlen(str) )
という行がありますが、これは
if(is_2byte(str[i]))
で十分だと思うのですが、如何でしょうか?
(実際、様々な文字列でテストしてみて、問題ありませんでした。)
投稿日時 - 2011-09-12 12:06:25
  • 回答No.8
レベル8

ベストアンサー率 44% (22/50)

No.2 です。 処理系によっては、ライブラリ関数が用意されている場合もあります。 例: LSI-C #include <jstring.h> char *jstrrev(char *s); 1バイトコードと2バイトコードが混在する 文字列s を文字単位で逆に並べかえます。 文字コードの判定では #include <jctype.h> ...続きを読む
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バイト目
補足コメント
MetalLover

お礼率 78% (349/443)

すみません。
僕の勘違いでした。
jstrrev関数は、含まれていませんでした。
投稿日時 - 2011-09-12 09:22:16
お礼コメント
MetalLover

お礼率 78% (349/443)

貴重な情報、ありがとうございます。

Borand C++ Compilerでは、jstrrev関数が存在しました。
一度この関数を試してみます。
投稿日時 - 2011-09-12 06:54:02
  • 回答No.7
レベル8

ベストアンサー率 44% (22/50)

No.2 です。 No.4 Tacosan さん > いったんバイト単位でひっくり返しておいて, > そのあとでおもむろに後ろから見ていけば > テンポラリ領域は不要だと思う. No.5 wormhole さん > 便宜上、ASCII文字をA、漢字の1バイト目をK1, 2バイト目をK2とします。 > > A K1 K2 > > これを反転すると ...続きを読む
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 単位で逆にするライブラリ関数*/
}
お礼コメント
MetalLover

お礼率 78% (349/443)

御丁寧な御説明、ありがとうございます。

記述して頂いた関数、試させていただきます。
投稿日時 - 2011-09-12 08:57:11
  • 回答No.11
レベル13

ベストアンサー率 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"; のようなデータがきた ...続きを読む
> 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のデータが来ることが前提であれば、このチェックは不要です。
お礼コメント
MetalLover

お礼率 78% (349/443)

御指摘ありがとうございます。

>j-1 >= 0 は
>char buf[2] = "\xe0";
>のようなデータがきた場合にメモリー破壊を回避する為の対処です。
なるほど、そういったケースもあり得ますよね。
思いつきませんでした。
投稿日時 - 2011-09-12 16:54:58
  • 回答No.5
レベル14

ベストアンサー率 29% (1088/3714)

>僕は馬鹿なので、文章を読んだだけでは、いまいちピンとこないのです。 文章を読んでわからないのであれば ノートに図をかくなりなんなりしながらわかるように 整理すればいいだけです。 便宜上、ASCII文字をA、漢字の1バイト目をK1, 2バイト目をK2とします。 A K1 K2 これを反転すると K2 K1 A これから先はご自分でご確認ください。 手順は#4の方が書かれていますよね。
>僕は馬鹿なので、文章を読んだだけでは、いまいちピンとこないのです。

文章を読んでわからないのであれば
ノートに図をかくなりなんなりしながらわかるように
整理すればいいだけです。

便宜上、ASCII文字をA、漢字の1バイト目をK1, 2バイト目をK2とします。

A K1 K2

これを反転すると

K2 K1 A

これから先はご自分でご確認ください。
手順は#4の方が書かれていますよね。
このQ&Aで解決しましたか?
関連するQ&A
-PR-
-PR-
こんな書き方もあるよ!この情報は知ってる?あなたの知識を教えて!
このQ&Aにはまだコメントがありません。
あなたの思ったこと、知っていることをここにコメントしてみましょう。

その他の関連するQ&A、テーマをキーワードで探す

キーワードでQ&A、テーマを検索する
-PR-
-PR-
-PR-

特集


関連するQ&A

-PR-

ピックアップ

-PR-
ページ先頭へ