using System;
using System.Text;
using System.Globalization;
public class Test
{
public static void Main()
{
var myString = @"This is a string before an emoji:🔹This is after the emoji.";
var teMyString = new StringInfo(myString);
Console.WriteLine("TextElements Substring:");
Console.WriteLine($"0-33:{teMyString.SubstringByTextElements(0, 33)}");
Console.WriteLine($"0-34:{teMyString.SubstringByTextElements(0, 34)}");
Console.WriteLine($"0-35:{teMyString.SubstringByTextElements(0, 35)}");
Console.WriteLine("Custom Xanatos's substring:");
Console.WriteLine($"0-33:{StringEx.UnicodeSafeSubstring(myString, 0, 33)}");
Console.WriteLine($"0-34:{StringEx.UnicodeSafeSubstring(myString, 0, 34)}");
Console.WriteLine($"0-35:{StringEx.UnicodeSafeSubstring(myString, 0, 35)}");
}
}
public static class StringEx
{
public static string UnicodeSafeSubstring(string str, int startIndex, int length)
{
if (str == null)
{
throw new ArgumentNullException("str");
}
if (startIndex < 0 || startIndex > str.Length)
{
throw new ArgumentOutOfRangeException("startIndex");
}
if (length < 0)
{
throw new ArgumentOutOfRangeException("length");
}
if (startIndex + length > str.Length)
{
throw new ArgumentOutOfRangeException("length");
}
if (length == 0)
{
return string.Empty;
}
var sb = new StringBuilder(length);
int end = startIndex + length;
var enumerator = StringInfo.GetTextElementEnumerator(str, startIndex);
while (enumerator.MoveNext())
{
string grapheme = enumerator.GetTextElement();
startIndex += grapheme.Length;
if (startIndex > length)
{
break;
}
// Skip initial Low Surrogates/Combining Marks
if (sb.Length == 0)
{
if (char.IsLowSurrogate(grapheme[0]))
{
continue;
}
UnicodeCategory cat = char.GetUnicodeCategory(grapheme, 0);
if (cat == UnicodeCategory.NonSpacingMark || cat == UnicodeCategory.SpacingCombiningMark || cat == UnicodeCategory.EnclosingMark)
{
continue;
}
}
sb.Append(grapheme);
if (startIndex == length)
{
break;
}
}
return sb.ToString();
}
}