C言語

日本語文字列を逆順に出力してみる-C言語

どーも、たかぽんです。

先日英語バージョンでは文字列の逆順表示をしました。

ですが、私たちが話す言語は日本語です!!!

日本語バージョンつくっていきましょう!

 

基本的な考え方は前回同様、後ろから順番に出力をしていく・・・。

であっているのですが、日本語は英語と違ってバイト数が変わってきます・・・。

しかも日本語は文字コードによって変動するっぽい・・・。

 

例えば環境でUTF-8という文字コードを使用していた場合、一文字3byte、別の文字コードでは2byteの可能性もある・・・といった風に・・・。

やっかいですね・・・。

例えば、情報が ”あいう” という日本語文字列があり、もしも日本語一文字2byteとして、仮に”あ”=”12″, ”い” = “23”, “う”=”45”,というデータだった場合、最初に入力された時の配列の中には”123456″という風にデータが入ります。

それを”ういあ”と逆順にしたいなら配列の中身を”563412″としなければいけません。

ちょっと言葉だけでは分かりづらいのでざっと書いたメモをお見せします。

このような感じです。

上を下にできればOKです!

各日本語に対応するバイトはかたまりとしてそのまま出力していく必要があります。

この時にバイト数が変わるとちょっとめんどうなんです・・・。

ちょっとでもずれたら出力は文字化けのようになってしまいます。。。

 

さて、前置きが長くなりましたが、改めて!

今回は日本語の文字列を逆順に出力するプログラムを作成します。(文字コードが多少変わっても大丈夫!ver!)

というわけで、やっていくのですが、完成版を先に載せておきます。

 

 

こんな感じです。

出力はこうなります。

Mac, UTF-8 日本語一文字3バイトの時

windows, Shift-JIS 日本語一文字2バイトの時

ちなみに、試しに1byte文字を6つ入れてみると・・・。

この環境では日本語一文字が3byteで、”123”,”456″がそれぞれ文字になっています。

その並びを崩さないように逆順にできていますね!(逆に1byte文字の場合逆順にはなりません)

そして文字コードが変わっても大丈夫そう・・・!

 

では早速簡単ですが説明を。

日本語の入力を受け取るところは前回書いた英数字の逆順プログラムのscanfがそのまま使用できます。

ただし、日本語だと少ない文字数でもすぐにバイト数が増えてしまうので、今回は100バイトと少しだけ想定される最大入力バイト数を多めに設定しています。

今回用意したデータとして、

  • 入力されたデータの総バイト数(データで配列がどこまでうまるのか?)
  • 環境ごとの日本語一文字のバイト数(文字コードが変わっても対応できるように)
  • 日本語での文字数(strlenではバイト数なので、日本語一文字あたりのバイトで総バイト数を割って求めます)

を用意しました。

次に逆順に出力をしていきます。

例えば、UTF-8の環境において”あいう”を逆順にしたい場合、日本語一文字は3byteで、合計総バイト数は9バイト、そして日本語の文字数は3文字です。

この時、各文字の先頭の配列の添字は、配列名をstrとすると、一文字目str[0]、二文字目str[3]、三文字目str[6]となります。

 

こんな感じですね。それぞれの先頭から文字のバイト数分(この例だと3byte)まとめて表示すれば大丈夫のはずですね。

そして順番的には一番後ろの文字から出力したいので、最初にstr[6]str[7]str[8]をまとめて出力し、次に先頭を日本語一文字分(3byte分)のバイト数ずらしてstr[3]str[4]str[5]をまとめて出力。

そして最後にもう一度同じようにずらして出力すれば逆順表示が出来上がります。

これを実際にプログラムにしていきます。

まず最初に、一番初めに出力する文字の先頭配列の添字を調べます。

上記画像のstr[6]が一番初めに出力されてほしいですね。そしてもしも2byteで3文字だった場合はstr[4]が一番初めに表示されてほしいはずです。

では、まずその最初に出力すべき位置をどうやって調べるのかをご説明します。

結論から言うと、

  • 総バイト数から最後の文字のバイトを引く
  • 日本語一文字のバイト数×本来の長さ-1

のどちらかの手法で求められることがわかります。

ちょっと難しいかもしれませんが、要するに最後の文字の先頭の配列よりも前のデータの個数を求めればよい!

ということです。

まだちょっとわからない・・・?

ではこちらの画像をご覧ください。

ちょっと小さくて申し訳ないのですが、データがたくさん入っていて、今は”う”の先頭の配列の添字を知りたいです。(今はずるして見えているのですがデータの番号は7,添字は[6]ですね。)

ここで、注目してほしいのが、7番のデータより前にあるデータの個数です。”1~6″の6個ですね?この時次の配列の添字は6になります。他の添字も然りですね。

要するに、今回、配列の添字を調べたい時は直前にあるデータの数を全て求めればいいのです。

その求め方として、

全体(1~9の9つのデータ)-最後(7,8,9のデータ)=6

9-3 = 6

日本語の文字数の合計(あ、い、うの3文字)から最後の文字分(うの一文字)の1を引いてそれに1文字分のバイト数(3)をかける。

(3-1)*3 = 6

二通りどちらでも大丈夫です。

前者の方法で書くなら、for分の定義文がこうなります。

 

 

これらの方法で最初に出力すべき位置を求め、for分に入る前に定義しておきます。

そして、for文は日本語の文字数と同じ回数回し(元が日本語3文字なら1文字づつの出力を3度でちょうど終わる)、都度先ほど求めた位置を文字数分前の配列へずらします(出力し終えた文字の分)。

こうすることで、

cs = 6,

cs = cs – 3 = 3,

cs = cs – 3 = 0

という風にそれぞれの文字の先頭の配列を指しながら移動することができます。

 

そして最後に、出力の部分ですが、先ほど調べたので、各文字の先頭の配列はわかっています。

その配列の添字に+1,+2をすることで、実際に二つや三つのデータをそのままの順番でまとめて出力します。

前にも言いましたが、日本語は複数バイトで表現されることが多く、それらを同時に出力させてやらないと文字化けしてしまいます。

たとえば、

こうしてしまうとうまく表示されません。

出力する順番を変えてもいけません。

また、今回は日本語一文字分のバイト数に対応できるかな?と

プログラム中で試しに一文字のバイト数を求めて調べるという方法を試してみました。

おそらくもっといい方法もありそうな気がしますが・・・。

ちょっと今の僕では手がでないかな・・・。

というか、この手法も載ってないからいい方法ではないかも・・・?

でもとりあえず、今の日本語は2,3,4byteあたりが多いそうなのでそれに対応するように出力部で各バイトごとにどうするのか?を記述しています。

正直見ててもっとスリムにかけそう・・・。

勉強不足ですねー・・・リファクタリング・・・。

 

はい、というわけでざざっと説明をさせていただきました。

日本語の文字がどういう風に配列に入れこまれるのか?っていうのが理解できればなんてことはないと思います!(結構てこずったw

そして手書きだとどうしても見づらいからもっといい方法探して見ますね・・・(^_^;)

あとはここまできたら次に見えてくるのは英字、日本語関係なく逆順ですが・・・。

これは難しそう・・・:(;゙゚’ω゚’):

今までの二つはちょっと考えればなんとなくは頭に思い浮かぶんですが・・・。

出力時にマルチバイト文字かどうかの判定をしていい感じにやればいけそうな気もしますが・・・。

詳細はがっつり考え込まないと無理っぽいですね・・・。

そのうちやってみようと思います。

 

それでは!!!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください