Ответ 1
Результат, возвращаемый StartsWith
, верен. По умолчанию большинство методов сравнения строк выполняют культурно-чувствительные сравнения, используя текущую культуру, а не обычные байтовые последовательности. Хотя ваш line
начинается с последовательности байтов, идентичной sub
, подстрока, которую она представляет, не эквивалентна в большинстве (или всех) культурах.
Если вам действительно нужно сравнение, которое обрабатывает строки как обычные байтовые последовательности, используйте перегрузку:
line.StartsWith(sub, StringComparison.Ordinal); // true
Если вы хотите, чтобы сравнение было нечувствительным к регистру:
line.StartsWith(sub, StringComparison.OrdinalIgnoreCase); // true
Вот более знакомый пример:
var line1 = "café"; // 63 61 66 E9 – precomposed character 'é' (U+00E9)
var line2 = "café"; // 63 61 66 65 301 – base letter e (U+0065) and
// combining acute accent (U+0301)
var sub = "cafe"; // 63 61 66 65
Console.WriteLine(line1.StartsWith(sub)); // false
Console.WriteLine(line2.StartsWith(sub)); // false
Console.WriteLine(line1.StartsWith(sub, StringComparison.Ordinal)); // false
Console.WriteLine(line2.StartsWith(sub, StringComparison.Ordinal)); // true
В приведенных выше примерах line2
начинается с той же последовательности байтов, что и sub
, за которой следует комбинационный острый акцент (U + 0301), который должен быть применен к окончательному e
. line1
использует прекомпонованный символ для é
(U + 00E9), поэтому его последовательность байтов не соответствует значению sub
.
В реальной семантике обычно не следует считать cafe
подстрокой café
; e
и é
рассматриваются как разные символы. То, что é
представляется в виде пары символов, начиная с e
, является внутренней деталью реализации схемы кодирования (Unicode), которая не должна влиять на результаты. Это демонстрируется вышеприведенным примером, противопоставляющим café
и café
; не ожидалось бы разного результата, если конкретно не было запланировано сравнение по порядку (побайтовое).
Адаптация этого объяснения к вашему примеру:
string line = "Mìng-dĕ̤ng-ngṳ̄"; // 4D EC 6E 67 2D 64 115 324 6E 67 2D 6E 67 1E73 304
string sub = "Mìng-dĕ̤ng-ngṳ"; // 4D EC 6E 67 2D 64 115 324 6E 67 2D 6E 67 1E73
Каждый символ .NET представляет собой код кода UTF-16, значения которого показаны в комментариях выше. Первые 14 блоков кода идентичны, поэтому сравнение char -by- char равно true (точно так же, как StringComparison.Ordinal
). Тем не менее, 15-й блок кода в line
является объединяющим макроном, ◌̄ (U + 0304), который сочетается с предыдущим ṳ
(U + 1E73), чтобы дать ṳ̄
.