今更な文字列長の話

相当に今更感があるのですが、文字列長の話です。
普通に考えれば、string.Lenth か、Len(String) 取ればいいんじゃね?と思うのですが、残念ながら…というお話です。

よーくMSDNを読んでみて下さい。
まずは、String.Length の解説
「プロパティ値」に「現在の文字列の文字数。」と書いてありますが、その下の「解説」の冒頭に、「Length プロパティは、このインスタンス内の Char オブジェクトの数を返します。Unicode 文字の数ではありません。」と書いてあります。

Char の数 ≠ 文字の数

ということです。これがどういうコトかは、後ほど改めて説明します。

お次は Len です。
こちらも「文字列内の文字数または変数を格納するために必要な公称バイト数を表す整数を返します。」と書かれています。この「または」以下がくせ者です-まるで法律の但し書き条項のようにくせ者です。
解説欄をよく読むと、まるでVB6以前のファイルの扱い方の解説を読んでいるような気分になります-FilePutなんて何時の話だと…。

どちらも何故こんな回りくどい言い回しをしているのか?何故素直に「文字列長」と言えないのか。
そもそも、VB.NETに移行することによって、 Len と LenB の混乱から逃れたのではなかったのか。

残念ながら、そうではないのです。
理由は大きく分けて二つあります。
一つは「サロゲートペア(代用対)」問題。Unicodeの16bitから溢れて、32bit(=Char 二つ分)で一文字を表す文字が存在することです。更に悪いことに、日本語にはそれにが居るということです。
もう一つは、他の言語で使われる「合成文字」です。こちらもコードとしては2byte以上(合成するだけ)必要ですが、表現する文字としては「1文字」です。濁点・半濁点を考えてもらえれば良いでしょう(実際、一部の体系ではそうですし)。

これらの問題を解決した上で「文字列」の長さをきちんと数えるには、System.Globalization.StringInfo を使用しなければいけないのです。

論より証拠。実際にやってみましょう。


こんなモノを用意してみます。一番上のテキストボックスに入れたTextの文字数を表示するというモノです。

コードはこんな感じです。ここでは実験が目的なので、何も考えずにコードビハインドに書きます。

    Private Sub cmdExec_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles cmdExec.Click
        Dim strTarget = Me.txtTest.Text
        '一つめのテキストボックスには、単純にString.Length
        Me.txtLength.Text = strTarget.Length.ToString()
        '二つ目はLEN
        Me.txtLength2.Text = Len(strTarget).ToString()
        '三つめは国際化対応
        Dim g As New System.Globalization.StringInfo(strTarget)
        Me.txtLength3.Text = g.LengthInTextElements.ToString
    End Sub

では、実行結果。

まずはとりあえず「あ」でテストしてみます。

どれも一文字と表示しています。

次はサロゲートペア(代用対)の問題です。
よく知られているのは「𠮟る」ですが、その辺りの一覧はコチラにあります。

見事に割れました。見ての通り、「文字数」としての正解は「1文字」なので、正しく値を返しているのは、System.Globalization.StringInfo のみということになります。

このサロゲートペア(代用対)は、確かに高頻度に現れる問題ではないとはいえ、ハマるとデカいという落とし穴になっています。コードを書く側もテストする側も、「お約束のヒトネタ」として押さえておくと良いでしょう。

#おまけ:[アクセサリ]→[システムツール]→[文字コード表]でも、Unicodeの直接入力が4文字まで(!)です。𠮟(U+20B9F)はどうするんだと…
#もう一つ。wordpress を使用しているのですが、素のママで𠮟(U+20B9F)を投入したら、そこで投稿が打ち切られました(内部でなんか落ちたのかなと…)。これで小一時間悩みました…。こうなりますと言うことで。

最後に、外国語の合成文字の問題です。
日本語の濁点・半濁点付きの文字も「一文字」として割り振られています。例えば、「が」は U+304C です。が、そうではない言語もあります。ここではベトナム語を取り上げます。(現代の)ベトナム語は、アルファベットと声調記号の合成で表現されます。例えば「ベトナム」のことは”Việt Nam”と書きますが、これの”ệ”は、実は合成文字で二文字です。が、実際上は1文字として認識されます。(「が」を二文字と数える日本人がどれだけ居ますか?というのと同じです。)

こちらもズレました
バイト数で切ると、文や語の途中で打ち切られる可能性があります。

と言う訳で、文字列長とバイト長は使い分けましょうというお話でした。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Spam Protection by WP-SpamFree