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

Этот код:

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

выходы:

Length a = 3
Length b = 4

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

8 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

Ваш персонаж по индексу 1 в "AC" такое суррогатная пара

Следует помнить, что суррогатные пары представляют 32-битные одиночные символы.

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

Console.WriteLine(char.IsSurrogatePair("AC", 1));

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

true если параметр s включает соседние символы в позициях index и index + 1, а числовое значение символа в позиции index находится в диапазоне от U+D800 до U+DBFF, а числовое значение символа в позиции index + 1 находится в диапазоне от U+DC00 через U+DFFF; иначе, false,

Это дополнительно объясняется в свойстве String.Length:

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

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

MSDN утверждает, что

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

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

var si = new StringInfo("AC");
Console.WriteLine(si.LengthInTextElements); // 3

Вы также можете перечислить каждый элемент текста, как это

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

С помощью foreach по струне разделим среднюю "букву" на две char объекты и напечатанный результат не будут соответствовать строке.

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

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

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

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

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

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

Юникод код указывает за пределы BMP представлены в UTF-16 с использованием 4-байтовых суррогатных пар.

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

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

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

То, что мы видим "на бумаге или экране" как одну букву, символ, глиф, символ или знак пунктуации, можно рассматривать как один элемент текста. Как описано в Стандартном приложении Unicode № 29 СЕГМЕНТАЦИЯ ТЕКСТА ЮНИКОДА, каждый текстовый элемент представлен одной или несколькими кодовыми точками. Исчерпывающий список кодов можно найти здесь.

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

Учитывая то, что мы теперь знаем, мы можем вывести, текстовый элемент может быть сохранен как один char как суррогатная пара из двух символов или, если текстовый элемент представлен несколькими кодовыми точками, некоторая комбинация отдельных символов и суррогатных пар. Как будто это не было достаточно сложно, некоторые текстовые элементы могут быть представлены различными комбинациями кодовых точек, как описано в Стандартном приложении № 15 к Unicode, ФОРМЫ НОРМАЛИЗАЦИИ ЮНИКОДА.


интерлюдия

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

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


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

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

using System.Globalization;

string a = "abc";
string b = "AC";

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

давая вывод,

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

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


Предостережение

.Net реализация текстовой сегментации Unicode в StringInfo а также TextElementEnumerator классы должны быть в целом полезными и, в большинстве случаев, будут давать ответ, который ожидает вызывающий объект. Однако, как указано в Приложении № 29 к стандарту Unicode, "цель сопоставления восприятий пользователя не всегда может быть достигнута именно потому, что один только текст не всегда содержит достаточно информации, чтобы однозначно определить границы".

Другие вопросы по тегам