Page 1
文字列をイテレーションする
Iterate over "elements" of a string.
第138回 PHP勉強会@東京
2019年5月29日 #phpstudy
公開日:
by USAMI Kenta @tadsan
に東京都渋谷区のGMO Yoursで開催された『第138回 PHP勉強会@東京』でライトニングトーク(5分)として発表しました。
文字列をイテレーションする
Iterate over "elements" of a string.
第138回 PHP勉強会@東京
2019年5月29日 #phpstudy
お前誰よ
うさみけんた (@tadsan) / Zonu.EXE
宣伝
先に
今回話す内容は
平成最後のLT大会で
発表した内容に由来
今回話す実装は
既にライブラリ化
しました
おさらい
イテレータ
とは何か
任意の処理を
繰り返しの構造 に落とし込める
繰り返し?
みんなだいすき
foreach
どうするの?
Iterator
インターフェイス
ジェネレータ
関数で
イテレータを
つくれる
時間がない
ので割愛
このあと
さんざん出ます
さて本題
文字列をイテレーションする
Iterate over "elements" of a string.
第138回 PHP勉強会@東京
2019年5月29日 #phpstudy
問題です
入力した文字列を 1文字ごとに改行 区切りで出力せよ
簡単じゃん
簡単でしょ?
😄
本当に?
🤔
(どう表示される かは環境による)
…………
🙃
どうしてこんな
ことがおこるのか
前提1:
PHPの は
string
「文字」を知らない
stringは
ただのバイト列
$s = 'abc'; echo $s[0];
これは先頭1文字ではなく
1バイトを取り出す
逆に言うとstring にはどんなバイナ
リも格納できる
例:
<?php
$s = file_get_contents('flower.png');
header('Content-Type: image/png');
echo $s;
mb関数が
あるのでは?
mb関数はencodingを常に
指定しないと
(環境依存せず)
確実に処理できない
🤔
PHP5.6以降では
の
default_charset
デフォルト値が"UTF-8"に、
が
mbstring.internal_encoding
非推奨になったのは嬉しい
それはencodingを明示的に 指定しなくても安全に処理
できることを意味しない
🙃
前提2:
UTF-8は
可変長バイト
ひとつの符号位置を
表すために
1〜4バイト
まだ日本語文字を
「2バイト文字」と 呼んだりしませんね?
Unicodeより
前の時代
Shift_JISでは
ガ (2バイト)
ガ (1バイト+1バイト)
この頃は半角カナ で情報量が半分に なることもあった
UTF-8では
ガ (3バイト)
ガ (3バイト+3バイト)
Unicode(UTF-8)で
表現すると
半角カナは単純に
バイト数が倍になる
たいへんですね
☺
その上で
Unicode時代の今日
何を「文字」と
呼ぶかを考える
1. バイトごと
2. コードポイントごと
3. 書記素クラスタごと
バイトごと
これがまさに
バイトごとの
イテレーション
これを
ジェネレータに
変換
ライブラリとしては
「foreachできるオブジェクト」
(=ジェネレータ)として提供
そうすることで 汎用的に利用で きるようになる
例:
数を集計する
コードポイント
ごと
コードポイント
(符号位置)
PHP はUnicodeを
(コア言語)
知らないが、PCRE関数
はUnicodeを
preg_match()
知ってる ( 修飾子)
/u
書記素クラスタ
ごと
書記素とは?
人間の目に
1文字に見える
単位
あ
漢
ガ
どれも単独の
書記素
ところでUnicodeに は「ガ」の複数の
表現方法が存在する
ガ (U+30AC)
ガ (U+30AB U+3099) ガ (U+FF76 U+FF9E)
結合したガ
ガ (U+30AC)
カ ゛
(結合文字)
+
ガ (U+30AB U+3099)
半角カ
+ ゙
ガ (U+FF76 U+FF9E)
Unicode正規化
余談: ファイル名に濁点のつい た文字をGitで管理するとMacと そのほかのファイルシステムで 諸々の厄介な事態が起こるので 禁忌。これはAppleのファイルシ ステムが伝統的にファイル名を Unicode正規化することが問題
(本筋から逸れ るので各自ggr)
書記素クラスタ のターンはまだ まだ終らないぜ
絵文字が
開いてしまった
パンドラの匣
Case1: 国旗
絵文字には
国旗が充実
%&’()*+,- ./0123456
……と
思うじゃん?
実態
国旗専用文字の[J] と[P]を並べて配置 すると に化ける
,
Case2: 家族
この絵文字のバイト数
strlen(' ');
この絵文字のバイト数
strlen(' ');
7
// => 25
この絵文字のUTF-8文字数
mb_strlen(' ', 'UTF-8');
この絵文字のUTF-8文字数
mb_strlen(' ', 'UTF-8');
7
// => 7
| | |
👨👩👦👦
Zero Width
Joiner
さて
そんな文字でも
一安心
ICU に依存
(intl)
(常に正しく計算するにはシ ステムに最新のUnicode定義
が実装されたICUが必要)
今回の話題は
ただのトリビアか?
No
Webアプリとしては 「文字数」を制限し
なければいけない
場合が多々ある
サービス都合の制限
ストレージ(DB)都合制限
自分がどういう理由で
文字数を制限しようと しているのか考えよう
例:
MySQLのインデックス長のデ フォルト設定が767バイトだか らutf8mb4のカラムの文字数を
VARCHAR(191)以下にしたい
例:
長いユーザー名は画面 に収まりきらないから
30文字以下にしたい
たいていは
mb_strlen($s, 'UTF-8')
で片がつく
「文字数」と「表示幅」は
全然違う概念なので厄介
(なので、今回紹介した
each_grapheme()を使って計算して
もうまくいかない)
オチはないけど各位 よく考えて「文字」
単位とつきあって
いきましょう
ちなみにTwitterの140 文字制限がDBの制約 ではないことは明白