Grapheme・Code Point・UTF-16・UTF-8を数える
文字数カウントツールで見た目の文字数、Unicode Code Point、UTF-16コード単位、UTF-8バイトを同時に確認できます。
結論:変換後の文字列を、利用先と同じ単位で数え直す
Mathematical Alphanumeric Symbolsの多くは補助平面にあり、1 Code PointをUTF-16のSurrogate Pair 2単位で表します。結合装飾はCode Point自体が増えます。
SNSやApplicationがGrapheme、Code Point、UTF-16、UTF-8 Byteのどれを採用するかはサービスごとに異なります。推測せず実際の入力欄でも確認します。
「1文字」の意味を仕様へ明記しないと、FrontendとBackendで上限判定がずれます。
制限単位を特定する
同じ文字列を4単位で数え、どの値が利用先のカウンターと一致するか確認します。
DBでは文字数上限とは別にUTF-8 Byte、Index長、Encodingの4-byte対応も確認します。
| 確認項目 | 確認方法 | 判断 |
|---|---|---|
| Grapheme | Intl.Segmenter | 見た目の文字数 |
| Code Point | Array.from/for...of | Unicode文字数 |
| UTF-16 | String.length | JavaScript長 |
| UTF-8 | TextEncoder | 保存・通信Byte |
文字数上限へ合わせる手順
- 通常文字と変換後の文字列を保存します。
- 文字数カウントツールへ変換後Textを貼ります。
- Grapheme・Code Point・UTF-16・UTF-8を記録します。
- 利用先の実カウンターと照合します。
- 上限を超える場合は装飾範囲または文字数を減らします。
変換前のASCII文字数をそのまま採用しません。
スタイルで増え方が違う
| 入力/結果 | Grapheme | Code Point | UTF-16 |
|---|---|---|---|
| A | 1 | 1 | 1 |
| 𝐀 | 1 | 1 | 2 |
| A̲ | 1 | 2 | 2 |
| 👩💻 | 1 | 3 | 5 |
CSSのフォント変更ではなくUnicode文字への置換
| 方法 | 文字列 | 内部で変わるもの |
|---|---|---|
| CSS Font | A → A | U+0041のままGlyphをFontで描き分ける |
| Fancy Unicode | A → 𝐀 | U+0041からU+1D400へCode Point自体を置換する |
| 結合装飾 | A → A̲ | U+0041の後ろへCombining Low Lineを追加する |
Mathematical Alphanumeric Symbolsは数式中の意味を区別するために定義された文字群です。SNS向けのおしゃれ文字は、それらを装飾目的で利用するケースが多く、CSSのfont-familyをコピーしているわけではありません。
NFC・NFD・NFKC・NFKDは目的を分けて使う
| 形式 | 主な処理 | 装飾文字への影響 |
|---|---|---|
| NFC | Canonical Decomposition後に可能な文字をComposition | 正準等価な表現をそろえるが、すべての装飾をASCIIへ戻すものではない |
| NFD | Canonical Decomposition | éをeとCombining Acute Accentへ分解する例がある |
| NFKC | Compatibility Decomposition後にComposition | 𝐀、𝔸、A、①等がAや1へ変わる場合がある |
| NFKD | Compatibility Decomposition | 互換文字を分解し、さらに結合列になる場合がある |
NFKCは一部のおしゃれ文字を普通の英数字へ近づける手段ですが、上下反転、Small Capsの近似文字、結合下線などを完全に元へ戻す汎用逆変換ではありません。元表記を保存する必要があるデータへ自動適用しません。
文字数はGrapheme・Code Point・UTF-16で分ける
| 数え方 | 𝐀の値 | 用途 |
|---|---|---|
| Grapheme Cluster | 1 | 利用者が見た目で認識する文字数 |
| Unicode Code Point | 1(U+1D400) | 文字の割り当て番号とUnicode処理 |
| JavaScript String.length | 2 UTF-16 Code Units | JavaScriptの添字・既存の入力制限 |
| UTF-8 | 4 Bytes | DB、API、File、通信時の保存容量 |
結合文字やZWJ Emojiは複数Code Pointでも1 Graphemeに見える場合があります。JavaScriptで見た目の文字数を数えるならIntl.Segmenter、Code Point単位ならfor...ofやArray.fromを使い、split("")だけに依存しません。
DevelopToolsで確認できる範囲と、できない範囲
| 項目 | 現行ツールの動作 |
|---|---|
| 入力 | 英字・数字を中心に変換し、日本語、絵文字、改行、未対応記号は削除せずそのまま残す |
| 21スタイル | Serif/Sansの太字・斜体、Monospace、Script、Fraktur、Double-Struck、Circled、Squared、Small Caps、上付き・下付き、上下反転、取り消し線・下線 |
| Unicode処理 | Intl.Segmenterが使える場合はGrapheme Cluster単位で処理し、使えない場合もArray.fromでSurrogate Pairを分割しない |
| 表示確認 | スタイルごとの変換文字数と対象文字数を表示し、未対応文字を見つけやすくする |
| 保存 | 各結果のコピー、TXT保存、お気に入り、最近コピーしたスタイルの並べ替えに対応 |
| 未対応 | 全角・括弧囲み、汎用的な逆変換、NFC/NFD/NFKC/NFKD実行、Unicode名・Code Point解析、Mixed Script/Confusable検出 |
| データ保持 | 入力文と変換結果はServerへ送信せず、LocalStorageへも保存しない。表示設定とお気に入りだけを端末内へ保存 |
文字を普通の英数字へ戻す、正規化結果を比較する、Code Pointを解析する場合はUnicodeコード表を併用します。文字数・UTF-8バイト・UTF-16コード単位を比べる場合は文字数カウントツールを使います。
表示互換性・検索性・アクセシビリティを確認する
- 利用先のFontにGlyphがなければ□や空白に見えるため、実際の端末とApplicationへ貼り付けて確認する
- プロフィール本文で表示できても、UsernameやIDの許可文字・文字数・正規化規則を満たすとは限らない
- 検索、読み上げ、音声入力、Copy後の再編集が必要な重要情報には通常文字を併記する
- Latin・Greek・Cyrillic等の見た目が似た文字を識別子へ混在させず、なりすましや誤認へ利用しない
- Password、URL、Email Address、Source Code、署名対象データへ装飾Unicodeを使わない
DevelopToolsで表示できることは、すべてのOS、SNS、Browser、Applicationで同じGlyphが表示される保証ではありません。Platformの仕様は変わるため、特定サービスで必ず使えるとは案内しません。
UnicodeとJavaScriptの一次資料
- Unicode UAX #15:Unicode Normalization Forms
- Unicode UAX #29:Unicode Text Segmentation
- Unicode UTS #39:Unicode Security Mechanisms
- Unicode:Mathematical Alphanumeric Symbols
- MDN:String.length
- MDN:String.prototype.normalize()
- MDN:Intl.Segmenter
NormalizationはUAX #15、Grapheme ClusterはUAX #29、ConfusableとIdentifier SecurityはUTS #39を基準にします。JavaScriptの実装はMDNのString.length、normalize()、Intl.Segmenterの説明と照合します。
具体例:10文字のASCIIを数学太字へ変換する
見た目は10文字でもUTF-16では英字1文字あたり2単位になる場合があります。
Grapheme=10、Code Point=10、UTF-16=20という結果になり得ます。
- 変換前を計測
- 太字へ変換
- 変換後を再計測
- 入力上限と照合
BackendがUTF-8 Byte上限ならUTF-16値だけでは判定できません。
よくある質問
- 変換した文字はすべての端末で同じように表示されますか?
- 保証できません。Code Pointが正しくても、OSやApplicationのFontがGlyphを持たなければ□になり、Fontの違いで字形も変わります。利用先で実表示を確認してください。
- 入力した文章はServerへ送信または保存されますか?
- 現行の特殊文字・おしゃれ文字変換ツールはBrowser内で変換し、入力文と結果をServerやLocalStorageへ保存しません。端末内へ保存するのは表示設定とお気に入りだけです。
- NFKCを実行すれば必ず普通の文字へ戻せますか?
- 戻せるのは互換分解が定義された文字です。上下反転、近似文字、独自Mapping、結合装飾等はLosslessに戻せない場合があるため、元文字列を保持してください。