C#(.Net)の部分文字列取得処理は String.SubString メソッド がありますが、これを文字列長ではなくバイト長で処理したいという要望が稀に良くあります。
日本だからね。仕方ないね。
ネット上に色々なサンプルがありますが、ここはシンプルにLinqで書き上げて見ましょう。
サンプルコード
using System.Linq;
using System.Text;
namespace MySample
{
public class StringUtil
{
public static string ByteSubString(string text, int byteLength, int startByteIndex = 0, Encoding encode = null)
{
// エンコード未指定時はShift-JISを仮置き
encode = encode ?? Encoding.GetEncoding("Shift-JIS");
var cutChars = text
.SkipWhile((x, i) => encode.GetByteCount(targetValue.Substring(0, i + 1)) <= startByteIndex)
.TakeWhile((x, i) => encode.GetByteCount(targetValue.Substring(0, i + 1)) <= byteLength)
.ToArray();
return new string(cutChars);
}
}
}
コード解説
簡単に解説をば。
まず考え方としては文字列を文字配列(char[])として捉えます。
文字配列を Enumerable.SkipWhile メソッド で開始位置のバイト数を満たすまで先頭から読み飛ばします。
その後、呼び飛ばした後の文字列を Enumerable.TakeWhile メソッド でバイト長を満たすまで取得します。
最後に取得したchar配列をstringのコンストラクタに投げてやれば文字列として返るので完成です。
汎用性を持たせるために、開始位置とか文字コードの指定は任意で出来るようにしてみました。
ここら辺はお好みでどーぞ。
Q&A
Q. 指定した開始位置が2バイト文字の間になる場合はどうなるの?
Skipの条件が「先頭から1文字ずつ確認し、開始位置を超えるまで」なので、指定バイト数より少なくSkipする事になります。
(例:"ほげふがぴよ" で開始位置指定が「3」の場合は "げふがぴよ"。実際に開始している位置は2バイト目になる。)
Q. 指定したバイト長が2バイト文字の間になる場合はどうなるの?
Takeの条件が「先頭から1文字ずつ確認し、指定バイト長を超えるまで」なので、指定バイト数より少なくTakeする事になります。
(例:"ほげふがぴよ" で取得バイト長が「5」の場合は "ほげ"。実際に返すバイト長は4バイトになる。)