目次
サンプル・プログラムの実行例
サンプル・プログラム
解説:文字数・行数のカウント
0167: /**
0168: * テキストの文字数・行数をカウント
0169: * ・半角・全角ともに1文字としてカウントする
0170: * ・行頭・行末の半角空白文字は文字数としてカウントしない
0171: * ・空行は1行とカウントする
0172: * @param string $sourテキスト
0173: * @return array(文字数,行数,ページ数)
0174: */
0175: function countText($sour) {
0176: $lines = 0;
0177: $chars = 0;
0178: $arr = preg_split("/[\n]/ui", $sour);
0179: foreach ($arr as $ln=>$str) {
0180: $str = trim($str); //行頭・行末の半角空白を除く
0181: if ($str == '') $lines++; //空行
0182: $n = mb_strlen($str); //文字数
0183: $chars += $n;
0184: $lines += ceil($n / CHARS_PER_LINE); //行数
0185: }
0186: $pages = ceil($lines / LINES_PER_PAGE); //ページ数
0187:
0188: return array($chars, $lines, $pages);
0189: }
ここではiPhone換算で、各々15を代入してある。
文字数・行数のカウントはユーザー定義関数 countText で行う。
入力されたテキストを関数 preg_split で行ごとに分解し、関数 trim で行頭・行末の半角空白文字を削除する。これは改行コードを省く作用もある。
ちなみに、PHP5.3で関数 split は廃止されているので、preg_splitを使うのが無難だ。
そして、各々の行に関数 mb_strlen を適用して文字数をカウントする。なぜ関数 strlen を用いないかは後述する。
なお、空行も1行としてカウントしている。
知りたい値は文字数、行数、ページ数の3つあるので、関数 array を使って配列の形で返している。
文字数のカウントについて
もし、文字数を組み込み関数 strlen でカウントするとしたら、全角文字の場合だけ2で割れば良い。
ところが、Unicodeや、Webコンテンツの標準となりつつあるutf-8の場合、単純に2で割ればいいというわけにはいかない。
試しに次のプログラムを実行してみてほしい。(サンプル・プログラムの圧縮ファイルに同梱されている)
関数 strlen と文字数
0001: <?php
0002: define('INTERNAL_ENCODING', 'utf-8');
0003: $encode = INTERNAL_ENCODING;
0004: echo <<< EOD
0005: <html lang="ja">
0006: <head>
0007: <meta http-equiv="Content-Type" content="text/html; charset={$encode}" />
0008: </head>
0009: <body>
0010: <pre style="font-size:xx-large;">
0011: EOD;
0012:
0013: $s1 = "A";
0014: $s2 = "α";
0015: $s3 = "剣";
0016: $s4 = "𠝏";
0017: printf("%s => strlen = %d<br />\n", $s1, strlen($s1));
0018: printf("%s => strlen = %d<br />\n", $s2, strlen($s2));
0019: printf("%s => strlen = %d<br />\n", $s3, strlen($s3));
0020: printf("%s => strlen = %d<br />\n", $s4, strlen($s4));
0021:
0022: echo <<< EOD
0023: </pre>
0024: </body>
0025: </html>
0026:
0027: EOD;
0028: ?>
半角文字 "A" は1バイト、全角文字 "α" は2バイト――ここまではいい。
ところが、漢字 "剣" は3バイト、その異体字である "𠝏" (Unicode: 0x2074F)に至っては4バイトにもなっている。
まず留意すべきことは、Unicodeが 2 バイトというのは誤解ということだ。なぜ誤解が生まれたかについては『ユニコード戦記』200ページに詳しい。
現時点で、Unicodeは最大4バイトまでが定義されている。これをビット・ストリームで表現するutf-8は、最大8バイトになる。つまり、「1文字=8バイト」という文字が存在するということだ。
また、名前は似ているが、utf-16は16ビット(2バイト)固定、utf-32は32ビット(4バイト)固定である点にも注意してほしい。
こうした背景を知っておくと、さきほどの関数 strlen の結果が理解できる。
大まかにいうと、utf-8は半角英数字は7ビット(1バイト)、その他のヨーロッパ文字は11~16ビット(2バイト)、かな漢字など欧米圏外の文字は21ビット(3バイト)、使われる頻度が少ない文字や古代文字などは26~32ビット(4バイト)といった形で割り当てられている。
このように1文字のビット数(バイト数)が可変になることをマルチバイト文字と呼んでいる。
では、マルチバイト文字の1文字を“1文字”としてカウントするにはどうしたらいいか――組み込み関数 mb_strlen の出番である。
関数 mb_strlen と文字数
0001: <?php
0002: define('INTERNAL_ENCODING', 'utf-8');
0003: mb_internal_encoding(INTERNAL_ENCODING);
0004: $encode = INTERNAL_ENCODING;
0005: echo <<< EOD
0006: <html lang="ja">
0007: <head>
0008: <meta http-equiv="Content-Type" content="text/html; charset={$encode}" />
0009: </head>
0010: <body>
0011: <pre style="font-size:xx-large;">
0012: EOD;
0013:
0014: $s1 = "A";
0015: $s2 = "α";
0016: $s3 = "剣";
0017: $s4 = "𠝏";
0018: printf("%s => mb_strlen = %d<br />\n", $s1, mb_strlen($s1));
0019: printf("%s => mb_strlen = %d<br />\n", $s2, mb_strlen($s2));
0020: printf("%s => mb_strlen = %d<br />\n", $s3, mb_strlen($s3));
0021: printf("%s => mb_strlen = %d<br />\n", $s4, mb_strlen($s4));
0022:
0023: echo <<< EOD
0024: </pre>
0025: </body>
0026: </html>
0027:
0028: EOD;
0029: ?>
ただし、事前に組み込み関数 mb_internal_encoding を使って文字エンコード方式を明示しておく必要はある。
ともかく、文字数をカウントする場合には関数 mb_strlen を使う と覚えておくとよいだろう。
また、PHPでマルチバイト文字(列)を扱う場合、エンコードはutf-8にしてmb_系およびpreg_系の関数を使うのが定石である。ereg 系の関数はPHP5.3で廃止されたので使わない方がいい。
余談:twitterはなぜ140文字なのか
ツイッターがSMS(ショートメッセージサービス。国際標準規格となっている)でも活用できるようにしたかったのです。SMSはヨーロッパをはじめ世界中に広まっています。 SMSの設けている字数制限は、世界共通で160文字です。メッセージの前にユーザーネームのスペースを確保したいと考え、140文字を私たちは基本設定としたのです。(121ページ)
ちょっと感動した。
すでに見てきたように、半角文字を1バイト(0.5文字)、全角文字を2バイト(1文字)と数えるのは日本の技術者のローカルルールだ。
twitterのように、半角も全角も1文字としてカウントするシステムが、これからのグローバル・スタンダードになるのだと思う。
参考サイト
- PHP5.3で変わったこと:ぱふぅ家のホームページ
- Unicode=古今東西のあらゆる文字を収録:ぱふぅ家のホームページ
参考書籍
ユニコード戦記 | |||
著者 | 小林龍生 | ||
出版社 | 東京電機大学出版局 | ||
サイズ | 単行本 | ||
発売日 | 2011年06月 | ||
価格 | 2,970円(税込) | ||
ISBN | 9784501549701 | ||
ドラえもん担当編集者が、数奇な運命でICT基盤を支える国際符号化文字集合の責任者になるまでの波瀾万丈。笑いと涙と友情を通して浮かび上がる情報通信技術標準戦争の現在。 | |||
つながる力 ツイッターは「つながり」の何を変えるのか? | |||
著者 | 勝間和代/広瀬香美 | ||
出版社 | ディスカヴァー・トゥエンティワン | ||
サイズ | 単行本 | ||
発売日 | 2009年12月 | ||
価格 | 1,430円(税込) | ||
ISBN | 9784887597747 | ||
「ツイッターって何?」「ミクシィや2ちゃんねるとどこがちがう?」「皆どうして夢中になるの?」ネット上を震憾させた凸凹コンビが、そんな疑問に全てお答えします。ビズ・ストーン(twitter共同創業者)×勝間和代、スペシャル対談&本社訪問レポートを特別収録。 | |||
そこで今回は、PHPを使って原稿の文字数・行数を数えるプログラムを作ってみることにする。シフトJIS、EUC-JP、Unicodeによってバイト数と文字数の関係が異なってくるので注意が必要だ。
(2021年7月10日)PHP8対応,リファラ・チェック追加,大幅改訂