こんにちは。
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);
}
__________________________________________________
この関数を、あいうえお順に試して行った所、ひらがなの
「あいうえお かきくけこ さしすせそ たちつてと なにぬねの はひふへほ まみむめ」
という文字列は、逆順にする事ができたのできました。
しかし、「も」以降の文字を含ませた途端、実行時エラーが発生し、プログラムがストップしてしまいます。
また、カタカナの場合は、「メ」以降の文字を含ませた場合に、実行時エラーとなります。
何が問題なのかが全然分からないので、困っています。
何かいい方法を知っておられる方がいらっしゃれば、是非アドバイスを頂きたいと思います。
では、よろしくお願い致します。
No.9ベストアンサー
- 回答日時:
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さんには、何度も御回答いただき、大変助けられました。
本当にありがとうございました。
No.11
- 回答日時:
> 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";
>のようなデータがきた場合にメモリー破壊を回避する為の対処です。
なるほど、そういったケースもあり得ますよね。
思いつきませんでした。
No.10
- 回答日時:
#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 …
御丁寧にありがとうございます。
是非参考にさせていただきます。
あと、No3で提示された関数において、
if(is_2byte(str[i]) && j-1>=0 && i+1 <= strlen(str) )
という行がありますが、これは
if(is_2byte(str[i]))
で十分だと思うのですが、如何でしょうか?
(実際、様々な文字列でテストしてみて、問題ありませんでした。)
No.8
- 回答日時:
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関数が存在しました。
一度この関数を試してみます。
No.7
- 回答日時:
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 単位で逆にするライブラリ関数*/
}
No.6
- 回答日時:
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以上のときだと思うのですが、
というような思い込みでプログラムを書くのは危険で、
デバッグに苦労することになります。
「どんなデータが入力されるかわからない」と想定して
対策を講じておくことが、後々、技能の向上に役立つと思いますよ。
御回答ありがとうございます。
上位バイトの範囲と下位バイトの範囲は、一部重なっているという事に、気が付きませんでした。
御指摘されたおかげで気が付きました。
No.5
- 回答日時:
>僕は馬鹿なので、文章を読んだだけでは、いまいちピンとこないのです。
文章を読んでわからないのであれば
ノートに図をかくなりなんなりしながらわかるように
整理すればいいだけです。
便宜上、ASCII文字をA、漢字の1バイト目をK1, 2バイト目をK2とします。
A K1 K2
これを反転すると
K2 K1 A
これから先はご自分でご確認ください。
手順は#4の方が書かれていますよね。
No.3
- 回答日時:
>しかし、「も」以降の文字を含ませた途端、実行時エラーが発生し、プログラムがストップしてしまいます。
>また、カタカナの場合は、「メ」以降の文字を含ませた場合に、実行時エラーとなります。
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がないので、少しお待ち下さい。)
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# c言語 int temp = 0; if(isdigit(arr[i])){ temp=arr[i] 2 2022/03/27 01:44
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# int temp = 0; if(isdigit(arr[i])){//文字が数字であれば(0~9) 1 2022/03/27 01:37
- C言語・C++・C# str[j++]の意味 2 2022/08/30 16:20
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- PHP c言語 文字 - '1'+26 3 2022/03/26 20:58
- Visual Basic(VBA) ExcelのVBAコードについて教えてください。 1 2022/04/01 12:11
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
fgetsなどのときのstdinのバッ...
-
ftoa の作り方
-
C言語のfor文です。 繰り返しの...
-
エンディアン:2バイトのデー...
-
絶対パスからのファイル名の切...
-
テキストデータをそのままバイ...
-
charでの計算?
-
strtol関数 自作
-
C言語の入力した文字を反転させ...
-
3桁区切(コンマ)記号をつけ...
-
「ポインタのポインタ」を使っ...
-
【C言語】文字型と整数型の違い
-
コマンドラインに入力されてい...
-
c言語配列の結合についてです。...
-
配列をnビットシフトする
-
str系関数を使わずに二つの文字...
-
【C言語】構造体内の領域解放(...
-
文字列から空白を取り除きたい...
-
atoi( ) の反対をやりたい
-
OpenCVで32bit ヘッダ無しRaw画...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
charからLPTSTRへの変換方法
-
charでの計算?
-
配列をnビットシフトする
-
'const char *' 型は 'char *' ...
-
型変換
-
テキストデータをそのままバイ...
-
文字列から空白を取り除きたい...
-
CStringをwchar_tに変換したい
-
絶対パスからのファイル名の切...
-
fgetsなどのときのstdinのバッ...
-
ネットワークにつながっている...
-
str系関数を使わずに二つの文字...
-
3桁区切(コンマ)記号をつけ...
-
atoi( ) の反対をやりたい
-
double型の値をchar配列に変換...
-
C言語のfor文です。 繰り返しの...
-
switch文で文字を比較すること...
-
ファイル名である文字列からbas...
-
c++ 文字列を入力して、一文字...
-
strncpyと_tcsncpy_sのヌルの扱...
おすすめ情報