文字列をイテレーションする

公開日:

東京都渋谷区GMO Yoursで開催された『第138回 PHP勉強会@東京』でライトニングトーク(5分)として発表しました。

Download PDF

スライドテキスト

Page 1

文字列をイテレーションする

Iterate over "elements" of a string.

第138回 PHP勉強会@東京

2019年5月29日 #phpstudy

Page 2

お前誰よ
うさみけんた (@tadsan) / Zonu.EXE

  • GitHub/Packagistでは id: zonuexe
    • ピクシブ株式会社 pixiv運営本部
  • Emacs Lisper, PHPer
  • Emacs PHP Modeのメンテナ引き継ぎました
    • 好きなリスプはEmacs Lispです
    • Qiitaに記事を書いたり変なコメントしてるよ

Page 3

宣伝

Page 4

Page 5

Page 6

先に

Page 7

今回話す内容は

平成最後のLT大会で

発表した内容に由来

Page 8

Page 9

今回話す実装は

既にライブラリ化
しました

Page 10

Page 11

おさらい

Page 12

イテレータ

とは何か

Page 13

任意の処理を

繰り返しの構造 に落とし込める

Page 14

繰り返し?

Page 15

みんなだいすき

foreach

Page 16

どうするの?

Page 17

Iterator
インターフェイス

Page 18

Page 19

Page 20

Page 21

ジェネレータ

Page 22

関数で

イテレータを

つくれる

Page 23

時間がない

ので割愛

Page 24

このあと

さんざん出ます

Page 25

さて本題

Page 26

文字列をイテレーションする

Iterate over "elements" of a string.

第138回 PHP勉強会@東京

2019年5月29日 #phpstudy

Page 27

問題です

Page 28

入力した文字列を 1文字ごとに改行 区切りで出力せよ

Page 29

簡単じゃん

Page 30

Page 31

Page 32

簡単でしょ?

😄

Page 33

Page 34

本当に?

🤔

Page 35

Page 36

(どう表示される かは環境による)

Page 37

…………

🙃

Page 38

どうしてこんな

ことがおこるのか

Page 39

前提1:

PHPの は

string
「文字」を知らない

Page 40

stringは

ただのバイト列

Page 41

$s = 'abc'; echo $s[0];

これは先頭1文字ではなく

1バイトを取り出す

Page 42

逆に言うとstring にはどんなバイナ
リも格納できる

Page 43

例:

<?php

$s = file_get_contents('flower.png');

header('Content-Type: image/png');
echo $s;

Page 44

mb関数が

あるのでは?

Page 45

Page 46

mb関数はencodingを常に
指定しないと

(環境依存せず)

確実に処理できない

🤔

Page 47

PHP5.6以降では

default_charset
デフォルト値が"UTF-8"に、

mbstring.internal_encoding
非推奨になったのは嬉しい

Page 48

それはencodingを明示的に 指定しなくても安全に処理
できることを意味しない

🙃

Page 49

前提2:

UTF-8は

可変長バイト

Page 50

ひとつの符号位置を

表すために
1〜4バイト

Page 51

まだ日本語文字を

「2バイト文字」と 呼んだりしませんね?

Page 52

Unicodeより
前の時代

Page 53

Shift_JISでは

ガ (2バイト)

ガ (1バイト+1バイト)

Page 54

この頃は半角カナ で情報量が半分に なることもあった

Page 55

UTF-8では

ガ (3バイト)

ガ (3バイト+3バイト)

Page 56

Unicode(UTF-8)で
表現すると

半角カナは単純に

バイト数が倍になる

Page 57

たいへんですね

Page 58

その上で

Unicode時代の今日

何を「文字」と

呼ぶかを考える

Page 59

1. バイトごと

2. コードポイントごと

3. 書記素クラスタごと

Page 60

バイトごと

Page 61

Page 62

これがまさに

バイトごとの

イテレーション

Page 63

これを

ジェネレータに
変換

Page 64

Page 65

Page 66

ライブラリとしては
「foreachできるオブジェクト」

(=ジェネレータ)として提供

Page 67

そうすることで 汎用的に利用で きるようになる

Page 68

Page 69

例:

数を集計する

Page 70

Page 71

コードポイント

ごと

Page 72

コードポイント

(符号位置)

Page 73

Page 74

PHP はUnicodeを

(コア言語)

知らないが、PCRE関数

はUnicodeを

preg_match()
知ってる ( 修飾子)

/u

Page 75

Page 76

書記素クラスタ

ごと

Page 77

書記素とは?

Page 78

人間の目に

1文字に見える
単位

Page 79

Page 80

どれも単独の
書記素

Page 81

ところでUnicodeに は「ガ」の複数の

表現方法が存在する

Page 82

ガ (U+30AC)

ガ (U+30AB U+3099) ガ (U+FF76 U+FF9E)

Page 83

結合したガ

ガ (U+30AC)

カ ゛

(結合文字)

+

ガ (U+30AB U+3099)

半角カ

+ ゙

ガ (U+FF76 U+FF9E)

Page 84

Unicode正規化

Page 85

余談: ファイル名に濁点のつい た文字をGitで管理するとMacと そのほかのファイルシステムで 諸々の厄介な事態が起こるので 禁忌。これはAppleのファイルシ ステムが伝統的にファイル名を Unicode正規化することが問題

Page 86

(本筋から逸れ るので各自ggr)

Page 87

書記素クラスタ のターンはまだ まだ終らないぜ

Page 88

絵文字が

開いてしまった

パンドラの匣

Page 89

Case1: 国旗

Page 90

絵文字には

国旗が充実

Page 91

%&’()*+,- ./0123456

Page 92

……と

思うじゃん?

Page 93

実態

Page 94

Page 95

国旗専用文字の[J] と[P]を並べて配置 すると に化ける

,

Page 96

Case2: 家族

Page 97

Page 98

この絵文字のバイト数

strlen(' ');

Page 99

この絵文字のバイト数

strlen(' ');

7
// => 25

Page 100

この絵文字のUTF-8文字数

mb_strlen(' ', 'UTF-8');

Page 101

この絵文字のUTF-8文字数

mb_strlen(' ', 'UTF-8');

7
// => 7

Page 102

Page 103

| | |
👨👩👦👦

Page 104

Zero Width
Joiner

Page 105

さて

Page 106

そんな文字でも

一安心

Page 107

Page 108

ICU に依存

(intl)
(常に正しく計算するにはシ ステムに最新のUnicode定義
が実装されたICUが必要)

Page 109

今回の話題は

ただのトリビアか?

Page 110

No

Page 111

Webアプリとしては 「文字数」を制限し
なければいけない

場合が多々ある

Page 112

サービス都合の制限

ストレージ(DB)都合制限

Page 113

自分がどういう理由で

文字数を制限しようと しているのか考えよう

Page 114

例:

MySQLのインデックス長のデ フォルト設定が767バイトだか らutf8mb4のカラムの文字数を
VARCHAR(191)以下にしたい

Page 115

例:

長いユーザー名は画面 に収まりきらないから
30文字以下にしたい

Page 116

たいていは

mb_strlen($s, 'UTF-8')

で片がつく

Page 117

「文字数」と「表示幅」は
全然違う概念なので厄介

(なので、今回紹介した
each_grapheme()を使って計算して
もうまくいかない)

Page 118

オチはないけど各位 よく考えて「文字」
単位とつきあって

いきましょう

Page 119

ちなみにTwitterの140 文字制限がDBの制約 ではないことは明白