以前「c#で、うまいこと全角を切ってくれるMidB関数」というのを書いたが、妙な判定をしなくても「指定バイト数の位置でぶった切ってゴミ文字が発生すれば、元の文字列より文字数が増える」
ということを使えばもっと綺麗にうまいこと全角を切ってくれる関数が作れることに気付いた。
しかも今度はエンコーディングに関係なく使える。
/// <summary> /// 指定位置から指定されたバイト数分の文字列を取得 /// </summary> /// <param name="stTarget">文字列</param> /// <param name="iStart">取得を開始する文字数 位置は0から</param> /// <param name="iLen">バイト数</param> /// <returns>取得した文字列</returns> /// <remarks></remarks> public static string MidB(string stTarget, int iStart, int iLen) { Encoding oEncoding = Encoding.GetEncoding("utf-8"); byte[] nByteAry = oEncoding.GetBytes(stTarget); // iLenに最大数が指定されていた場合、オーバーフローして正しい計算ができなくなるので // 代わりに最大数 / 2 をバイト数として利用する if (iLen == int.MaxValue) { iLen = int.MaxValue / 2; } // 開始が最大文字数より後ろだった場合、空文字を戻す if (nByteAry.Length < (iStart + 1)) { return ""; } // nLenが最大文字数を超えないように調整 if (nByteAry.Length < iStart + iLen) { iLen = nByteAry.Length - iStart; } // 最初の文字が全角の途中で切れていた場合の調整 int iFixStart = iStart; while (CheckJustSplitMultiByte(stTarget, iFixStart, oEncoding) == false) { iFixStart += 1; } // 最後の文字が全角の途中で切れていた場合の調整 int iFixEnd = iStart + iLen; while (CheckJustSplitMultiByte(stTarget, iFixEnd, oEncoding) == false) { iFixEnd -= 1; } // 指定バイト数取りだし return oEncoding.GetString(nByteAry, iFixStart, iFixEnd - iFixStart); } /// <summary> /// 指定バイト数の位置がマルチバイト文字の途中かを判定する /// </summary> public static bool CheckJustSplitMultiByte(string stTarget, int iSplitPosB, Encoding oEncoding) { byte[] nByteAry = oEncoding.GetBytes(stTarget); // 分割位置が文字列範囲外の場合、ぴったり切れている if (iSplitPosB < 0 || nByteAry.Length <= iSplitPosB) { return true; } // 分割して合計文字数を得る。マルチバイト区切りで発生したゴミ文字があれば文字数は増える string stSplitLeft = oEncoding.GetString(nByteAry, 0, iSplitPosB); string stSplitRight = oEncoding.GetString(nByteAry, iSplitPosB, nByteAry.Length - iSplitPosB); int iSplitCharSum = stSplitLeft.Length + stSplitRight.Length; return (stTarget.Length == iSplitCharSum); }
ただし、タイ文字やハングル文字のような最初のバイトだけでも文字として成立するけれど、次のバイトを加えると別の文字になるというものは正しく判定できないと思う。
……現地の技術者はどうやって解決してるんだこの問題。