がりっちさんに4バイト文字の𩹉(とびうお)を送り付けて、がりっちさんが作ってるTwitterクライアントの文字列処理をばぐらせて遊んでいたら、𩹉(とびうお)問題という名前がつきました。
StringInfoクラス使えば便利メソッドあるから簡単じゃん?と思ったら
SubstringByTextElementsという、今回のがりっちさんの中で重要なメソッドがなくなってたみたいです。自力実装されてたコードを見て自分だったら…というのを書いてみました。
publicstaticclass StringExtensions { /// <summary>/// 文字数を返す/// </summary>/// <paramname="self">対象の文字列</param>/// <returns>文字数</returns>publicstaticint LengthInTextElements(thisstring self) { returnnew StringInfo(self).LengthInTextElements; } /// <summary>/// 1文字ずつ返す感じのIEを作る/// </summary>/// <paramname="self">対象の文字列</param>/// <paramname="index">開始位置</param>/// <returns>IE</returns>publicstatic IEnumerable<string> GetTextElementEnumerable(thisstring self, int index = 0) { var e = StringInfo.GetTextElementEnumerator(self, index); while (e.MoveNext()) { yieldreturn (string)e.Current; } } /// <summary>/// 指定した範囲の文字列を切り出す/// </summary>/// <paramname="self">対象の文字列</param>/// <paramname="startIndex">開始位置</param>/// <paramname="length">長さ</param>/// <returns></returns>publicstaticstring SubstringByTextElements(thisstring self, int startIndex, int length) { var sub = self.GetTextElementEnumerable(startIndex).Take(length).ToArray(); if (sub.Length != length) { thrownew ArgumentOutOfRangeException(); } returnstring.Concat(sub); } }
とりあえずGetTextElementEnumeratorというメソッドがあるので、それを使ってIE
[TestClass] publicclass UnitTest1 { [TestMethod] publicvoid LengthInTextElements() { var target = "俺が𩹉だ!"; Assert.AreEqual(5, target.LengthInTextElements()); } [TestMethod] publicvoid GetTextElementEnumerable() { var target = "俺が𩹉だ!"; var result = target.GetTextElementEnumerable().ToArray(); Assert.AreEqual("俺", result[0]); Assert.AreEqual("が", result[1]); Assert.AreEqual("𩹉", result[2]); Assert.AreEqual("だ", result[3]); Assert.AreEqual("!", result[4]); } [TestMethod] publicvoid SubString() { var target = "俺が𩹉だ!"; var result = target.SubstringByTextElements(2, 1); Assert.AreEqual("𩹉", result); } [TestMethod] publicvoid SubString_範囲外() { try { var target = "俺が𩹉だ!"; var result = target.SubstringByTextElements(2, 1000); Assert.Fail(); } catch (Exception ex) { // Lengthがはみ出てると例外が出る Assert.IsInstanceOfType(ex, typeof(ArgumentOutOfRangeException)); } } }