アプリ版:「スタンプのみでお礼する」機能のリリースについて

こんにちは。

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);
}
__________________________________________________

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

何が問題なのかが全然分からないので、困っています。
何かいい方法を知っておられる方がいらっしゃれば、是非アドバイスを頂きたいと思います。
では、よろしくお願い致します。

A 回答 (11件中1~10件)

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 単位で逆にするライブラリ関数*/
}

この回答への補足

上記の関数を試したところ、上手く行きました。
YanenoSuzumeさんには、何度も御回答いただき、大変助けられました。
本当にありがとうございました。

補足日時:2011/09/12 09:23
    • good
    • 0
この回答へのお礼

御指摘ありがとうございます。
では、修正して試させていただきます。

お礼日時:2011/09/12 06:56

> 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のデータが来ることが前提であれば、このチェックは不要です。
    • good
    • 0
この回答へのお礼

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

>j-1 >= 0 は
>char buf[2] = "\xe0";
>のようなデータがきた場合にメモリー破壊を回避する為の対処です。
なるほど、そういったケースもあり得ますよね。
思いつきませんでした。

お礼日時:2011/09/12 16:54

#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 …
    • good
    • 0
この回答へのお礼

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

あと、No3で提示された関数において、
if(is_2byte(str[i]) && j-1>=0 && i+1 <= strlen(str) )
という行がありますが、これは
if(is_2byte(str[i]))
で十分だと思うのですが、如何でしょうか?
(実際、様々な文字列でテストしてみて、問題ありませんでした。)

お礼日時:2011/09/12 12:06

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バイト目

この回答への補足

すみません。
僕の勘違いでした。
jstrrev関数は、含まれていませんでした。

補足日時:2011/09/12 09:22
    • good
    • 0
この回答へのお礼

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

Borand C++ Compilerでは、jstrrev関数が存在しました。
一度この関数を試してみます。

お礼日時:2011/09/12 06:54

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 単位で逆にするライブラリ関数*/
}
    • good
    • 0
この回答へのお礼

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

記述して頂いた関数、試させていただきます。

お礼日時:2011/09/12 08:57

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以上のときだと思うのですが、

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

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

上位バイトの範囲と下位バイトの範囲は、一部重なっているという事に、気が付きませんでした。
御指摘されたおかげで気が付きました。

お礼日時:2011/09/12 09:25

>僕は馬鹿なので、文章を読んだだけでは、いまいちピンとこないのです。



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

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

A K1 K2

これを反転すると

K2 K1 A

これから先はご自分でご確認ください。
手順は#4の方が書かれていますよね。
    • good
    • 0

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

    • good
    • 0
この回答へのお礼

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

申し訳ございませんが、可能なら具体的なコードを記述していただけませんでしょうか?
僕は馬鹿なので、文章を読んだだけでは、いまいちピンとこないのです。

お礼日時:2011/09/11 05:09

>しかし、「も」以降の文字を含ませた途端、実行時エラーが発生し、プログラムがストップしてしまいます。


>また、カタカナの場合は、「メ」以降の文字を含ませた場合に、実行時エラーとなります。
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);
}

この回答への補足

上記の関数、試させていただきました。
見事に上手く行きました。
本当に感謝します。

補足日時:2011/09/12 09:26
    • good
    • 0
この回答へのお礼

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

記述していただいた関数を、一度試してみて、結果を報告します。
(今、手元に自分のPCがないので、少しお待ち下さい。)

お礼日時:2011/09/11 05:11

このプログラムでは、 j=0 のときに temp[-1] を書きつぶしているのではないですか?


プログラム中で確保していない領域ですので、他に影響が出ると思いますが。
    • good
    • 0
この回答へのお礼

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

strの最終文字は、2バイト文字の上位バイトではあり得ないので、
is_2byte(temp[j])が1を返すのは、jが1以上のときだと思うのですが、
間違っていますでしょうか?

お礼日時:2011/09/11 05:05

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています