Edited at

【PHP】半角カナで固定長データを作るときの落とし穴

ダンプファイルを作成しようとして固定長データを取得する際に、半角カナ混じりのデータだと引っかかりがちな落とし穴があります。


PHP

   $address = "オオサカシスミノエク";

$address2 = "サッポロシアツベツク";

まずは、この2つの変数の文字数を調べてみましょう。文字数を調べる時はmb_strlenを使います。


PHP

   echo mb_strlen($address); //10と表示されます

echo mb_strlen($address2); //濁点、半濁点ともに1文字となるので、12と表示されます。

では、それを踏まえた上で、このデータを40バイトの固定長で値を作成してみます(今回はわかりやすく、空白部分を*で埋めてみます)。固定長データを作る際には便利なstr_padという関数があるのでこれを使ってみます。

すると…


PHP

   echo str_pad($address,40,"*"); // オオサカシスミノエク*********** と表示される。

echo str_pad($address2,40,"*"); //サッポロシアツベツク**** と表示される。

想定していたデータは、オオサカシスミノエク******************************のはずなのに、明らかに記号が足りません。サッポロシアツベツクの場合だと、もっと記号が少なくなっています。


落とし穴1:UTF-8の半角カナは3バイト

では、バイト数を取得できるstrlen関数を使って調べてみます。


PHP

   echo strlen($address); //30と表示されます。

echo strlen($address2); //36と表示されます。

バイト数が、文字数の3倍になっているのがわかります。このように、まず注意しなければいけないのは、UTF-8において、半角カナは1文字あたり3バイトであること、そしてstr_pad関数の引数はバイト数で換算しないといけない、ということです。よって、UTF-8の場合は、半角1文字あたり3バイトなので10*3の30バイト分消費しているため、残りの記号は10文字分しかパディングされないのです。

そのため、str_pad関数を使う場合は、固定長の値も調節が必要になります。半角カナを使うたびに、3バイト消費、言い換えれば2バイト余分に消費しているので、2*半角カナの文字数分、加算しておきます。


PHP

   echo str_pad($address,40 + mb_strlen($address)*2,"*"); //オオサカシスミノエク******************************

echo str_pad($address2,40 + mb_strlen($address2)*2,"*") //サッポロシアツベツク****************************

このように、半角カナ数の2倍分だけ固定長データを加算しておけば、動的な値の変化にも対応できます。

※なお、シフトJISの場合は2バイトになりますので、半角カナの文字分だけ加算してください。


落とし穴2:半角スペース混じりの半角カナ

では、先程の変数をこのようにしてみます。


PHP

   $address = "オオサカシ スミノエク";

$address2 = "サッポロ シ アツベツ ク";

この2つの変数の文字数を調べてみましょう。スペースが増えた分だけ増えているはずです。


PHP

   echo mb_strlen($address); //11と表示されます

echo mb_strlen($address2); //15と表示されます。

では、同じようにこのデータを40バイトの固定長で値を作成してみます

すると…


PHP

   echo str_pad($address,40,"*"); // オオサカシ スミノエク********* と表示される。

echo str_pad($address2,40,"*"); // サッポロ シ アツベツ ク* と表示される。

先程と比べるとなにか不可解な結果になっています。

では、バイト数を取得できるstrlen関数を使って調べてみます。


PHP

   echo strlen($address); //31と表示されます。

echo strlen($address2); //39と表示されます。

おわかりでしょうか?半角カナは3バイトなのに対し、半角スペースは1バイトのままなのです。したがって、先ほどと同じように固定長データを作ろうとすると、想定外の現象にでくわします。


PHP

   str_pad($address,40 + mb_strlen($address)*2,"*"); //62バイトの想定に対し、61バイトとなる

str_pad($address,40 + mb_strlen($address2)*2,"*"); //70バイトの想定に対し、67バイトとなる

下手に、半角スペースも含めて半角カナ全部を加算してしまうとこうなってしまい、想定していた固定長が不足してしまいます。したがって、半角スペースも含む場合で固定長バイト数を追加する場合は、半角カナの文字数だけを加算しないといけないので、一度、半角スペースを除外した状態で文字数をカウントする必要があります。


PHP

   str_pad($address,40 + mb_strlen(str_replace(" ","",$address))*2,"*"); //40文字62バイト       

str_pad($address,40 + mb_strlen(str_replace(" ","",$address2))*2,"*"); //40文字70バイト

これで、正しく固定長データを取得することができました。

参考にしたページ