Vistaからでしょうか。追加された4バイト文字とか2バイトに入りきらない文字たち。こいつら、stringとcharではうまく扱えません。
例を見てみましょう。
こんなXAMLを用意します。
<Page x:Class="App37.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App37"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><TextBox x:Name="TextBox"TextChanged="TextBox_TextChanged" /><TextBlock x:Name="TextBlock" /><TextBlock x:Name="TextBlockLength" /></StackPanel></Page>
そして、TextChangedに以下のようなコードを書きます。テキストの長さと、1文字ずつばらした内容を出力するといった感じです。
using Windows.UI.Xaml.Controls; namespace App37 { publicsealedpartialclass MainPage : Page { public MainPage() { this.InitializeComponent(); } privatevoid TextBox_TextChanged(object sender, TextChangedEventArgs e) { var result = ""; for (int i = 0; i < this.TextBox.Text.Length; i++) { result += $"[{this.TextBox.Text[i]}]"; } this.TextBlock.Text = result; this.TextBlockLength.Text = this.TextBox.Text.Length.ToString(); } } }
実行すると以下のような感じに動くプログラムです。
ここに𩹉とか🐑とか🍖とかを書き込むと残念な結果になります。文字数も出力もだめだめです。
🐑とかがcharでいう2つぶんのサイズになってるっぽいですね。こういうのを正しく扱うためにStringInfoというクラスがあります。 このクラスでstringをくるんでやることで、文字数を正しくカウントできるようになります。LengthInTextElementsプロパティがそれになります。コードを書き換えてみましょう。
using System.Globalization; using Windows.UI.Xaml.Controls; namespace App37 { publicsealedpartialclass MainPage : Page { public MainPage() { this.InitializeComponent(); } privatevoid TextBox_TextChanged(object sender, TextChangedEventArgs e) { var stringInfo = new StringInfo(this.TextBox.Text); var result = ""; for (int i = 0; i < stringInfo.LengthInTextElements; i++) { result += $"[{this.TextBox.Text[i]}]"; } this.TextBlock.Text = result; this.TextBlockLength.Text = stringInfo.LengthInTextElements.ToString(); } } }
文字数は改善しましたが、1文字単位に分割するところがまだうまくいってません。これを改善するには、StringInfoのGetNextTextElementメソッドを使えばよさそうに見えます。これを使うと文字列とインデックスを指定するといい感じに、文字を返してくれます。
以下のように修正してみましょう。
using System.Globalization; using Windows.UI.Xaml.Controls; namespace App37 { publicsealedpartialclass MainPage : Page { public MainPage() { this.InitializeComponent(); } privatevoid TextBox_TextChanged(object sender, TextChangedEventArgs e) { var stringInfo = new StringInfo(this.TextBox.Text); var result = ""; for (int i = 0; i < stringInfo.LengthInTextElements; i++) { result += $"[{StringInfo.GetNextTextElement(this.TextBox.Text ,i)}]"; } this.TextBlock.Text = result; this.TextBlockLength.Text = stringInfo.LengthInTextElements.ToString(); } } }
実行すると惜しい感じになります。
このとき指定するindexは、何文字目かではなく、素のstringのindexなので4バイト文字とかがあるとずれてしまいます。正しいindexを取得するには、StringInfoクラスのParseCombiningCharactersメソッドを使って取得します。 このメソッドの戻り値に、何番目の文字列がstringのindexの何個目かという情報が入っています。こんな感じで使います。
using System.Globalization; using Windows.UI.Xaml.Controls; namespace App37 { publicsealedpartialclass MainPage : Page { public MainPage() { this.InitializeComponent(); } privatevoid TextBox_TextChanged(object sender, TextChangedEventArgs e) { var stringInfo = new StringInfo(this.TextBox.Text); // 1文字単位に分割するのに必要なindexを取得 var index = StringInfo.ParseCombiningCharacters(this.TextBox.Text); var result = ""; for (int i = 0; i < stringInfo.LengthInTextElements; i++) { // ParseCombiningCharactersの戻り値からindexを取得する result += $"[{StringInfo.GetNextTextElement(this.TextBox.Text ,index[i])}]"; } this.TextBlock.Text = result; this.TextBlockLength.Text = stringInfo.LengthInTextElements.ToString(); } } }
これで実行するとめでたく正しい表示が出来るようになります。
まとめ
4バイト文字とかが入ってても正しい文字数を取得するにはStringInfoでくるんでLengthInTextElementsプロパティを使う。1文字単位で文字を取得するにはStringInfo.ParseCombiningCharactersとStringInfo.GetNextTextElementを組み合わせて使う。ということでした。