Почему длина этой строки больше, чем количество символов в ней?

Этот код:

string a = "abc";
string b = "A𠈓C";
Console.WriteLine("Length a = {0}", a.Length);
Console.WriteLine("Length b = {0}", b.Length);

выходы:

Length a = 3
Length b = 4

Почему? Единственное, что я мог представить, это то, что китайский символ имеет длину 2 байта и метод .Length возвращает количество байтов.

Ответ 1

Все остальные дают поверхностный ответ, но есть и более глубокое обоснование: количество "символов" является трудным для определения вопроса и может быть удивительно дорогостоящим для вычисления, тогда как свойство length должно быть быстрым.

Почему это сложно определить? Ну, есть несколько вариантов, и ни один из них не действительно более прав, чем другой:

  • Количество блоков кода (байт или другой блок данных фиксированного размера, С# и Windows обычно используют UTF-16, так что он возвращает количество двухбайтовых частей), безусловно, имеет значение, поскольку компьютер все еще нуждается в решении с данными в этой форме для многих целей (например, для записи в файл, заботятся о байтах, а не о символах)

  • Число кодовых точек Unicode довольно легко вычислить (хотя O (n), потому что вы должны сканировать строку для суррогатных пар) и может иметь значение для текстового редактора.... но на самом деле это не то же самое как количество символов, напечатанных на экране (называемых графемами). Например, некоторые акцентированные буквы могут быть представлены в двух формах: один кодовый номер или две точки в паре вместе, один из которых обозначает букву, а один - "добавить акцент на письмо моего партнера". Будет ли пара двух символов или одна? Вы можете нормализовать строки, чтобы помочь с этим, но не все допустимые буквы имеют одно представление с кодовым названием.

  • Даже количество графем не совпадает с длиной печатной строки, которая зависит от шрифта среди других факторов, и поскольку некоторые символы печатаются с некоторым перекрытием во многих шрифтах (кернинг), длина строки на экране не обязательно равна сумме длины графем в любом случае!

  • Некоторые точки Юникода - это даже не символы в традиционном смысле, а скорее какой-то контрольный маркер. Как маркер порядка байтов или индикатор справа налево. Считаете ли вы эти цифры?

Короче говоря, длина строки на самом деле является смехотворно сложным вопросом, и вычисление ее может занять много времени процессора, а также таблицы данных.

Более того, что точка? Почему эти показатели важны? Ну, только вы можете ответить на это по вашему делу, но лично я считаю, что они вообще неактуальны. Ограничение ввода данных, которое я нахожу, более логично выполняется ограничениями байтов, так как это то, что нужно передать или сохранить в любом случае. Ограничить размер дисплея лучше всего с помощью программного обеспечения на стороне дисплея - если у вас есть 100 пикселей для сообщения, сколько символов вы вписываетесь, зависит от шрифта и т.д., Которое в любом случае не известно программному обеспечению уровня данных. Наконец, учитывая сложность стандарта unicode, вы, вероятно, будете иметь ошибки в крайних случаях, если вы попробуете что-нибудь еще.

Так что это сложный вопрос, когда не используется много общего назначения. Количество блоков кода тривиально для вычисления - это всего лишь длина базового массива данных - и наиболее значимый/полезный как общее правило с простым определением.

Вот почему b имеет длину 4 за пределами объяснения поверхности, потому что в документации так сказано.

Ответ 2

Из документации свойства String.Length:

Свойство Length возвращает количество объектов Char в этом экземпляре, а не число символов Юникода. Причина в том, что символ Unicode может быть представлен более чем одним Char. Используйте класс System.Globalization.StringInfo для работы с каждым символом Юникода вместо Char.

Ответ 3

Ваш персонаж с индексом 1 в "A𠈓C" является SurrogatePair

Ключевым моментом для запоминания является то, что суррогатные пары представляют 32-разрядныйодиночные символы.

Вы можете попробовать этот код, и он вернет True

Console.WriteLine(char.IsSurrogatePair("A𠈓C", 1));

Char. Метод IsSurrogatePair (String, Int32)

True, если параметр s содержит соседние символы в позициях индекс и индекс + 1, а числовое значение символа в диапазоны позиций от U + D800 до U + DBFF, а числовые значение символа в индексе позиции + 1 варьируется от U + DC00 до U + DFFF; в противном случае false.

Это объясняется в String.Length свойстве:

Свойство Length возвращает число Char объектов в этом instance, а не число символов Unicode. Причина в том, что Символ Юникода может быть представлен более чем одним Char. Использовать Класс System.Globalization.StringInfo для работы с каждым Unicode вместо каждого Char.

Ответ 4

Как указывали другие ответы, даже если есть 3 видимых символа, они представлены объектами 4 char. Вот почему Length равен 4, а не 3.

MSDN заявляет, что

Свойство Length возвращает количество объектов Char в этом instance, а не число символов Unicode.

Однако, если вы действительно хотите знать количество "текстовых элементов", а не количество объектов char, вы можете использовать класс StringInfo.

var si = new StringInfo("A𠈓C");
Console.WriteLine(si.LengthInTextElements); // 3

Вы также можете перечислить каждый текстовый элемент следующим образом

var enumerator = StringInfo.GetTextElementEnumerator("A𠈓C");
while(enumerator.MoveNext()){
    Console.WriteLine(enumerator.Current);
}

Использование foreach в строке будет разделять среднюю букву в двух объектах char, и результат печати не будет соответствовать строке.

Ответ 5

Это связано с тем, что свойство Length возвращает количество объектов char, а не число символов в Юникоде. В вашем случае один из символов Юникода представлен более чем одним объектом char (SurrogatePair).

Свойство Length возвращает количество объектов char в этом экземпляр, а не число символов Юникода. Причина в том, что Символ Unicode может быть представлен более чем одним Char. Использовать Класс System.Globalization.StringInfo для работы с каждым Unicode вместо каждого Char.

Ответ 6

Как говорили другие, это не количество символов в строке, а количество объектов Char. Символом 𠈓 является кодовая точка U + 20213. Поскольку значение находится вне 16-разрядного диапазона типов Char, оно кодируется в UTF-16 в качестве суррогатной пары D840 DE13.

Способ получения длины в символах упоминался в других ответах. Однако его следует использовать с осторожностью, так как существует много способов представления символа в Юникоде. "à" может быть 1 скомпонованным персонажем или 2 символами (+ диакритики). Может потребоваться нормализация, как в случае twitter.

Вы должны прочитать это
Абсолютный минимум Каждый разработчик программного обеспечения Абсолютно, положительно должен знать о Unicode и наборах символов (нет оправданий!)

Ответ 7

Это связано с тем, что length() работает только для кодовых точек Unicode, размер которых не превышает U+FFFF. Этот набор кодовых точек известен как Basic Multilingual Plane (BMP) и использует только 2 байта.

Кодовые точки Unicode вне BMP представлены в UTF-16 с использованием пар байтов с 4 байтами.

Чтобы правильно подсчитать количество символов (3), используйте StringInfo

StringInfo b = new StringInfo("A𠈓C");
Console.WriteLine(string.Format("Length 2 = {0}", b.LengthInTextElements));

Ответ 8

Хорошо, в .Net и С# все строки закодированы как UTF-16LE. A string сохраняется как последовательность символов. Каждый char инкапсулирует память объемом 2 байта или 16 бит.

То, что мы видим "на бумаге или экране" в виде одной буквы, символа, символа, символа или знака пунктуации, можно рассматривать как отдельный текстовый элемент. Как описано в Unicode Standard Annex # 29 UNICODE TEXT SEGMENTATION, каждый текстовый элемент представлен одним или несколькими кодовыми точками. Полный список кодов может быть найден здесь.

Каждая точка кода должна быть закодирована в двоичном формате для внутреннего представления компьютером. Как указано, каждый char хранит 2 байта. Кодовые точки на уровне или ниже U+FFFF могут храниться в одном char. Точки кода выше U+FFFF сохраняются как суррогатная пара, используя два символа для представления одной кодовой точки.

Учитывая то, что мы теперь знаем, мы можем вывести, текстовый элемент можно сохранить как один char, как суррогатная пара двух символов или, если текстовый элемент представлен несколькими кодовыми точками как комбинация отдельных символов и суррогатных пар. Как будто это было недостаточно сложно, некоторые текстовые элементы могут быть представлены различными комбинациями кодовых точек, как описано in, Unicode Standard Annex № 15, UNICODE NORMALIZATION FORMS.


Интерлюдия

Итак, строки, которые выглядят одинаково при визуализации, могут фактически состоять из различной комбинации символов. Порядковый (байтовый байт) сравнение двух таких строк обнаружил бы разницу, это может быть неожиданным или нежелательным.

Вы можете повторно кодировать строки .Net. так что они используют ту же форму нормализации. После нормализации две строки с одинаковыми текстовыми элементами будут закодированы одинаково. Для этого используйте функцию string.Normalize. Однако, помните, некоторые разные текстовые элементы выглядят похожими друг на друга.: -s


Итак, что все это значит по отношению к вопросу? Текстовый элемент '𠈓' представлен единым расширением унифицированных идеограмм кода U + 20213 cjk b. Это означает, что он не может быть закодирован как одиночный char и должен быть закодирован как суррогатная пара, используя два символа. Вот почему string b - это char дольше, чем string a.

Если вам нужно надежно (см. caveat) подсчитать количество текстовых элементов в string, вы должны использовать System.Globalization.StringInfo, как это.

using System.Globalization;

string a = "abc";
string b = "A𠈓C";

Console.WriteLine("Length a = {0}", new StringInfo(a).LengthInTextElements);
Console.WriteLine("Length b = {0}", new StringInfo(b).LengthInTextElements);

выводящий результат,

"Length a = 3"
"Length b = 3"

как ожидалось.


Caveat

Реализация. Unicode Text Segmentation в StringInfo и TextElementEnumerator должны быть в целом полезными и в большинстве случаев будут давать ответ, который ожидает абонент. Однако, как указано в Unicode Standard Annex # 29," цель сопоставления восприятия пользователя не всегда может быть выполнена именно потому, что только текст не всегда содержит достаточно информации для однозначного определить границы.