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