Twitter limits Tweet length to 140 characters making the definition of a “character” and how they are counted central to any Twitter application. このページでは、ツイート文字制限のためにTwitterサーバ側でどのようなコードで文字をカウントしているかを説明します。 このページの例は主にRubyで記載されていますが、コンセプト自体は全てのプロミング言語で適用可能です。
Unicodeを処理したことのあるプログラマにとっては、 the short answer to the question is that Tweet length is measured by the number of codepoints in the NFC normalized version of the text. それがあまりにもギークすぎる場合は、以降の我々の説明を参考にしたほうが良いでしょう。 ギーク過ぎない場合は、 一緒に Twitterで働きましょう。
URLが文字のカウントにどのような影響を与えるかについては、 How Twitter wraps URLs with t.co を参照してください。
API経由でやり取りする全ての Twitter の属性は、UTF-8 エンコードされたテキストが使用できます。データが破損しないようにするため、それ以外の全てのエンコーディングは、Teitterへ送信する前に UTF-8 へ変換しなければなりません。
Wikipedia には 文字 (computing) の記事がありますが、これが専門的なもので、意図的に曖昧な定義になっています。ここで我々が言う定義とは、コンピューティングにおける一般的な文字の定義ではなく、“140 文字”と言った場合の“文字”が何を意味しているのかという定義です。
多くのツイートの場合、全ての文字は1バイトなのでこのページを読んでもあまり役に立ちません。ツイート内の文字数とはテキスト文のバイト長と同じです。 基本文字や数字や句読点以外のものを使用する場合は、そう単純ではなくなります。 多くの人々はマルチバイトの漢字文字で問題を起こると思っているでしょうが、Twitter ではむしろアクセント母音で最も混乱を引き起こすと突き止めました。 英語を使う人は、アクセント母音が普通に動作すると思っているようです。 以下の例を見てください: he word “café”. It turns out there are two byte sequences that look exactly the same, but use a different number of bytes:
café | 0x63 0x61 0x66 0xC3 0xA9 | Using the “é” character, called the “composed character”. |
café | 0x63 0x61 0x66 0x65 0xCC 0x81 | Using the combining diacritical, which overlaps the “e” |
上記で説明した “café” の問題は、ユーザーがツイートの “café”を何文字と数えるかという疑問を発生させます。 人の目から見ればこれは明らかに4文字です。データの表記方法によっては、これはUTF-8で5バイトか6バイトになります。 Twitter does not want to penalize a user for the fact we use UTF-8 or for the fact that the API client in question used the longer representation. Therefore, Twitter does count “café” as four characters no matter which representation is sent.
Nearly all user input methods automatically convert the longer combining mark version into the composed version but the Twitter API cannot count on that. Even if we did ignore that the byte length of the “é” character is two bytes rather than the one you would expect. Below there is some more specific information on how to get that information out of Ruby/Rails but for now I’ll cover the general concepts that should be available in any language.
The Unicode Standard covers much more that a listing of characters with numbers associated. Unicode does provide such a list of “codepoints” (more info), which is the U+XXXX
notation you sometimes see. The Unicode Standard also provides several different ways to encode those codepoints (UTF-8 and UTF-16 are examples, but there are others). The Unicode standard also provides some detailed information on how to deal with character issues such as Sorting, Regular Expressions and of importance to this issue, Normalization.
So, back in the café, the issue of multiple byte sequences having the same on-screen representation was breezed right by. There is an entire section of the Unicode tables devoted to the “Combining Diacritical Marks” (see that Unicode “block” here). These are not stand-alone characters but instead the additional “diacritical marks” used in addition to other base characters in many languages. For example the ¨ over the ü, common to German; or the ˜ over the ñ in Spanish. There are a great many combinations needed to cover all languages in the world so Unicode provides some simple building blocks, the Combining Diacritical Marks.
For the most common characters (like é, ü and company) there is also a character just for the combination. The reasons for that are mostly historical but since they exist it’s something we’ll always need to be aware of. This historical oddity is the exact reason for the two “café” representations. If you look back at the representations you’ll see one uses 0x65 0xCC 0x81
, where 0x65
is simply the letter “e” and >0xCC 0x81
is the Combining Diacritical Mark for ´
. Since there are multiple ways to represent the same thing using Unicode the Unicode Standard provides information on how to normalize the multiple different representations.
The Unicode Standard provides information on several different kinds of normalization, Canonical and Compatibility. There is a full description of the different options in the Unicode Standard Annex #15, the report on normalization. The normalization report is 32 pages and covers the issue in great detail. Reproducing the entire report here would be of very little use so instead we’ll focus on what normalization Twitter is using.
Twitter counts the length of a Tweet using the Normalization Form C (NFC) version of the text. This type of normalization favors the use of a fully combined character (0xC3 0xA9
from the café example) over the long-form version (0x65 0xCC 0x81
). Twitter also counts the number of codepoints in the text rather than UTF-8 bytes. The 0xC3 0xA9
from the café example is one codepoint (U+00E9
) that is encoded as two bytes in UTF-8, whereas 0x65 0xCC 0x81
is two codepoints encoded as three bytes.
Ruby 1.8 では、ActiveSupport::Multibyte
クラスによってマルチバイト文字がサポートされています。
That class provides a method for Unicode Normalization but unfortunately the results of the length
method are not intuitive. For some examples check out http://gist.github.com/159484 … this was actually the script used when troubleshooting character counting issues in the Twitter code base. After the experimentation shown in the gist above we settled on the following code for checking Tweet length (minus other comments surrounding it, which are a short version of this page):
ユニコードコンソーシアム、Javaと使ってどのように正規化するかを例示しています。そのオリジナルコードでデモがhttp://unicode.org/reports/tr15/Normalizer.htmlで見られます。
World Wide Web Consortium (W3C) が、Perlで記述された文字を正規化するためのコマンドラインツールを提供しています。 このツールの最新版の情報は http://www.w3.org/International/charlintで見られます。
PHP では、正規化はNormalizer クラスで実行できます。