サロゲートペアを1文字として認識するよう、LenB, LeftB, MidB, RightB の例にならい、LenU, LeftU, MidU, RightU を実装します。
#region サロゲートペアを意識した文字列操作関数
public static int LenU(string str)
{
if (string.IsNullOrEmpty(str)) return 0;
int lengthC = str.Length;
int lengthU = 0;
int i = 0;
while (i < lengthC)
{
char c = str[i];
if (i + 1 < lengthC && char.IsSurrogatePair(c, str[i + 1]))
{
lengthU++;
i += 2;
}
else
{
lengthU++;
i += 1;
}
}
return lengthU;
}
public static string LeftU(string str, int length)
{
return MidU(str, 1, length);
}
public static string MidU(string str, int start)
{
int length = LenU(str) - start + 1;
return MidU(str, start, length);
}
public static string MidU(string str, int start, int length)
{
if (string.IsNullOrEmpty(str)) return string.Empty;
// 開始位置を検索
int lengthC = str.Length;
int skipLength = 0;
int pos = 0;
start = start - 1;
while (pos < lengthC && skipLength < start)
{
char c = str[pos];
if (pos + 1 < lengthC && char.IsSurrogatePair(c, str[pos + 1]))
{
skipLength++;
pos += 2;
}
else
{
skipLength++;
pos += 1;
}
}
// 開始位置以降の文字を取得
StringBuilder builder = new StringBuilder();
int lengthU = 0;
while (pos < lengthC && lengthU < length)
{
char c = str[pos];
if (pos + 1 < lengthC && char.IsSurrogatePair(c, str[pos + 1]))
{
builder.Append(c);
builder.Append(str[pos + 1]);
lengthU++;
pos += 2;
}
else
{
builder.Append(c);
lengthU++;
pos += 1;
}
}
return builder.ToString();
}
public static string RightU(string str, int length)
{
if (length <= 0) return string.Empty;
int lengthU = LenU(str);
if (length > lengthU) length = lengthU;
int pos = lengthU - length + 1;
return MidU(str, pos, length);
}
#endregion