Runtime: Представляем System.Rune

Созданный на 16 сент. 2017  ·  106Комментарии  ·  Источник: dotnet/runtime

Навеяно обсуждением здесь:

https://github.com/dotnet/corefxlab/issues/1751

Одна из проблем, с которыми сталкивается .NET при поддержке Unicode, заключается в том, что он основан на дизайне, который в настоящее время устарел. Мы представляем символы в .NET с помощью System.Char , которое является 16-битным значением, которого недостаточно для представления значений Unicode.

Разработчикам .NET необходимо узнать о загадочных суррогатных парах:

https://msdn.microsoft.com/en-us/library/xcwwfbb8 (v=vs.110).aspx

Разработчики редко используют эту поддержку, в основном потому, что они недостаточно знакомы с Unicode, не говоря уже о том, что может предложить им .NET.

Я предлагаю ввести тип System.Rune , который поддерживается 32-битным целым числом и соответствует кодовой точке, а эквивалентный тип rune в C# должен быть псевдонимом этого типа.

rune станет предпочтительной заменой char и послужит основой для правильной обработки Unicode и строк в .NET.

Что касается названия руны, вдохновение исходит от Го:

https://blog.golang.org/strings

В разделе «Кодовые точки, символы и руны» дается объяснение, краткая версия:

«Кодовая точка» — это слишком многословно, поэтому Go вводит более короткий термин для этой концепции: руна. Термин появляется в библиотеках и исходном коде и означает то же самое, что и «кодовая точка», с одним интересным дополнением.

Обновление Теперь у меня есть реализация System.Rune здесь:

https://github.com/migueldeicaza/NStack/blob/master/NStack/unicode/Rune.cs

Со следующим API:

public struct Rune {

    public Rune (uint rune);
    public Rune (char ch);

    public static ValueTuple<Rune,int> DecodeLastRune (byte [] buffer, int end);
    public static ValueTuple<Rune,int> DecodeLastRune (NStack.ustring str, int end);
    public static ValueTuple<Rune,int> DecodeRune (byte [] buffer, int start, int n);
    public static ValueTuple<Rune,int> DecodeRune (NStack.ustring str, int start, int n);
    public static int EncodeRune (Rune rune, byte [] dest, int offset);
    public static bool FullRune (byte [] p);
    public static bool FullRune (NStack.ustring str);
    public static int InvalidIndex (byte [] buffer);
    public static int InvalidIndex (NStack.ustring str);
    public static bool IsControl (Rune rune);
    public static bool IsDigit (Rune rune);
    public static bool IsGraphic (Rune rune);
    public static bool IsLetter (Rune rune);
    public static bool IsLower (Rune rune);
    public static bool IsMark (Rune rune);
    public static bool IsNumber (Rune rune);
    public static bool IsPrint (Rune rune);
    public static bool IsPunctuation (Rune rune);
    public static bool IsSpace (Rune rune);
    public static bool IsSymbol (Rune rune);
    public static bool IsTitle (Rune rune);
    public static bool IsUpper (Rune rune);
    public static int RuneCount (byte [] buffer, int offset, int count);
    public static int RuneCount (NStack.ustring str);
    public static int RuneLen (Rune rune);
    public static Rune SimpleFold (Rune rune);
    public static Rune To (Case toCase, Rune rune);
    public static Rune ToLower (Rune rune);
    public static Rune ToTitle (Rune rune);
    public static Rune ToUpper (Rune rune);
    public static bool Valid (byte [] buffer);
    public static bool Valid (NStack.ustring str);
    public static bool ValidRune (Rune rune);
    public override bool Equals (object obj);

    [System.Runtime.ConstrainedExecution.ReliabilityContractAttribute((System.Runtime.ConstrainedExecution.Consistency)3, (System.Runtime.ConstrainedExecution.Cer)2)]
    protected virtual void Finalize ();
    public override int GetHashCode ();
    public Type GetType ();
    protected object MemberwiseClone ();
    public override string ToString ();

    public static implicit operator uint (Rune rune);
    public static implicit operator Rune (char ch);
    public static implicit operator Rune (uint value);

    public bool IsValid {
        get;
    }

    public static Rune Error;
    public static Rune MaxRune;
    public const byte RuneSelf = 128;
    public static Rune ReplacementChar;
    public const int Utf8Max = 4;

    public enum Case {
        Upper,
        Lower,
        Title
    }
}

Обновить известные проблемы

  • [x] Некоторые API выше принимают uint, нужно взять Rune.
  • [ ] Необходимо реализовать семейство IComparable
  • [ ] RuneCount/RuneLen нужны лучшие имена, см. документы (возможно, они должны быть Utf8BytesNeeded?)
  • [ ] Выше API «ustring» ссылаются на мой API UTF8, на самом деле это не часть API, но мы должны рассмотреть, есть ли в некоторых из них шлюз к System.String или к Utf8String.
api-needs-work area-System.Runtime up-for-grabs

Самый полезный комментарий

Я сказал это в оригинальном выпуске и скажу это снова. Отказ от того, что говорит стандарт, потому что вам не нравится эта фраза, запутает больше, чем решит, и, учитывая, что в Unicode есть кодовая страница рун, это только еще больше запутает.

Имя неправильное.

Все 106 Комментарий

Ожидаете ли вы, что представление в памяти будет состоять из строк 32-битных объектов или будет транслироваться на лету? А как насчет удвоения памяти, если первое? Каково влияние на производительность, если последнее?

Является ли хорошей идеей назвать технологию, связанную с Unicode, в честь определенного скрипта, поддерживаемого Unicode (и технологию улучшения поддержки астральных планов, в честь скрипта BMP)?

Я думаю, что предложение (и, возможно, его нужно сделать более явным) состоит в том, что представление строк в памяти вообще не меняется. Тип Rune просто представляет отдельную 21-битную кодовую точку (хранящуюся как 32-битное целое число). Вместо этого методы, ссылающиеся на кодовые точки , могут возвращать Rune . Предположительно в string есть некоторая функциональность, позволяющая перечислять Rune .

Я думаю, что есть пара очевидных моментов, по которым нам нужно достичь консенсуса для чего-то вроде этого:

  1. Есть ли существенная ценность в создании типа Rune вместо использования Int32 , как это делают текущие методы?
  2. Является ли слово «руна» действительно хорошим выбором?

Чтобы ответить на (1), я думаю, нам нужно более полное описание того, как будут выставлены Rune , какие методы будут получать и возвращать их и т. д. И определить, лучше ли это, чем иметь дело с Int32 вместо этого.

Что касается (2), я сам немного сомневаюсь. «Руна» — это своего рода эзотерическое слово в английском языке, и его использование в этом контексте имеет несколько необычных коннотаций. Есть также момент, который поднимают другие: он противоречит другой концепции Unicode. Когда я выполняю поиск «Unicode Rune», я получаю в основном результаты для блока Runic Unicode и лишь некоторые из документации по языку Go.

char — это и половина слова, и полное слово; и вы должны осмотреть его окружение, чтобы определить, какой из них представляет собой половину буквы или целую букву.

Возможно, System.character , где всегда полная буква... :sunglasses:

char - это немного ужасное представление и даже для языков ascii/latin; рост эмодзи все еще будет распространяться; это означает, что char - это проверка и, возможно, проверка следующего типа char

@NickCraver в твиттере

В то время как utf8 является кодировкой переменной ширины; редко (если вообще?) пользователь хочет иметь дело с половинными символами; как для utf8, так и для utf32.

32-битный тип хорошо подходит для перечисления.

Более сложными будут indexOf, Length и т. д. для производительности или памяти.

  1. массив байтов является лучшим представлением для непрозрачного формата; например, сохранение формата в его исходном формате или в окончательном формате (передача файлов, передача по проводам и т. д.)
  2. массив байтов является лучшим представлением пропускной способности памяти и размера памяти
  3. байтовый массив согласуется с Position и indexOf, Length и т. д. с точки зрения байтов

Однако, когда вы начинаете заботиться о реальных символах, верхнем регистре, разделении на символы; понимая, что такое символ, байт становится переменной ширины. Char не делает это лучше; он удваивает размер самых маленьких символов; включает больше символов, но по-прежнему имеет переменную ширину.

Для этого 32-битное значение может быть очень полезным с точки зрения пользовательского кода. Однако у него есть проблемы с позицией, длиной и второстепенными элементами (indexOf и т. д.).

Мне очень нравится строка только ascii и строка utf8 «Реализация компактной строки» https://github.com/dotnet/coreclr/issues/7083; для быстрой обработки только строк ascii

Однако, вопреки всему, о чем я там спорил... Интересно, на что будет похоже 32-битное представление utf8? Позиция сопоставляется с позицией; поиск символов будет быстрым, как в ascii, элементы имеют собственные размеры и т. д., как он будет сочетаться с обработкой каждого байта или символа для определения его размера?

Преобразование туда и обратно будет дороже; так что это был бы скорее формат обработки; чем формат хранения.

@migueldeicaza , как я понимаю, вы имеете в виду только расширение односимвольного формата с 16-битного char до 32-битного, поэтому все представления содержатся в значении; а не возможность половинного значения - а не обязательно внутренний формат.

Однако следует учитывать некоторые моменты (т. е. отношение положения, стоимость поиска и т. д.)

Кроме того, Swift также работает с целыми символьными форматами .

Swift предоставляет несколько различных способов доступа к Unicode-представлениям строк. Вы можете выполнить итерацию по строке с помощью оператора for-in, чтобы получить доступ к ее отдельным значениям символов в виде расширенных кластеров графем Unicode. Этот процесс описан в разделе Работа с персонажами.

В качестве альтернативы можно получить доступ к значению String в одном из трех других представлений, совместимых с Unicode:

  • Набор единиц кода UTF-8 (доступ к которому осуществляется с помощью свойства строки utf8)
  • Набор единиц кода UTF-16 (доступ к которому осуществляется с помощью свойства строки utf16)
  • Набор 21-битных скалярных значений Unicode, эквивалентных форме кодировки строки UTF-32 (доступ к которой осуществляется с помощью свойства строки unicodeScalars).

Я сказал это в оригинальном выпуске и скажу это снова. Отказ от того, что говорит стандарт, потому что вам не нравится эта фраза, запутает больше, чем решит, и, учитывая, что в Unicode есть кодовая страница рун, это только еще больше запутает.

Имя неправильное.

@mellinoe

Rune предоставит многие операции, которые сегодня вы ожидаете от Char, такие как ToLower[Invariant], ToUpper[Invariant], ToTitle, IsDigit, IsAlpha, IsGraphic, IsSymbol, IsControl.

Кроме того, он будет предоставлять такие вещи, как:

  • EncodeRune (кодирует руну в байтовый буфер)
  • RuneUtf8Len (возвращает количество байтов, необходимых для кодирования руны в UTF8),
  • IsValid (не все значения Int32 допустимы)

И взаимодействие со строкой, и Utf8string по мере необходимости.

Я перенес/настроил поддержку строки Go в .NET, и она предлагает представление о том, как будет выглядеть этот мир (это без какой-либо помощи во время выполнения):

https://github.com/migueldeicaza/NStack/tree/master/NStack/юникод

@benaadams сказал:

Интересно, на что будет похоже 32-битное представление utf8? Позиция сопоставляется с позицией; поиск символов будет быстрым, как в ascii, элементы имеют собственные размеры и т. д., как он будет сочетаться с обработкой каждого байта или символа для определения его размера?

UTF8 — это представление в памяти, которое будет продолжать существовать и оставаться представлением (и, надеюсь, это более долгосрочная внутренняя кодировка для будущих строк в .NET).

Вы должны декодировать существующие строки UTF16 (System.String) или будущие строки UTF8 (Utf8String) не в символы (по той причине, по которой вы и я согласны), а в руны.

Некоторые примеры преобразования строки Utf8 в руны:

https://github.com/migueldeicaza/NStack/blob/6a071ca5c026ca71c10ead4f7232e2fa0673baf9/NStack/strings/ustring.cs#L756

Содержит ли строка utf8 руну:

https://github.com/migueldeicaza/NStack/blob/6a071ca5c026ca71c10ead4f7232e2fa0673baf9/NStack/strings/ustring.cs#L855

Я только что заметил, что не реализовал индексатор ("Дайте мне n-ю руну")

Скорость доступа к N-й руне в строке зависит от хранилища, а не от самой Руны. Например, если ваше хранилище UTF32, у вас есть прямой доступ к каждой руне. Это академично, так как никто не использует это. Доступ к N-му элементу в UTF16 и UTF8 требует правильного сканирования элементов, составляющих строку (байты или 16-битные целые числа), для определения правильной границы. Не путать с String[int n] { get; } , которая просто возвращает n-й символ, независимо от правильности.

@benaadams Быстрый персонаж - это уровень выше руны. Символы в Swift представляют собой «расширенные кластеры графем», состоящие из одной или нескольких рун, которые при их объединении создают удобочитаемый символ.

Таким образом, символ Swift не имеет фиксированного 32-битного размера, он имеет переменную длину (и у нас также должна быть эта конструкция, но она принадлежит другому типу данных). Вот пример с этой страницы, но это также распространяется на настройку оттенка смайликов:

Вот пример. Буква é может быть представлена ​​как одна скалярная буква Юникода é (СТРОЧНАЯ ЛАТИНСКАЯ БУКВА E С ОСТРОЙ, или U+00E9). Однако одна и та же буква может также быть представлена ​​как пара скаляров — стандартная буква e (ЛАТИНСКАЯ СТРОЧНАЯ БУКВА E или U+0065), за которой следует скаляр COMBINING ACUTE ACCENT (U+0301). Скаляр COMBINING ACUTE ACCENT графически применяется к скаляру, который предшествует ему, превращая e в é, когда он визуализируется системой рендеринга текста, поддерживающей Unicode.

Просто для меня слово grapheme было бы более самоописывающим.

Мои два цента за имя, снова цитируя сообщение Go о строках с акцентом:

« Кодовая точка » — это слишком многословно, поэтому Go вводит более короткий термин для этой концепции: руна. Этот термин появляется в библиотеках и исходном коде и означает то же самое, что и «кодовая точка» , с одним интересным дополнением.

Я на 100% согласен с @blowdart , называть это руной просто сбивает с толку и неправильно. В стандарте Unicode кодовые точки упоминаются три раза только на первой странице вводной главы , но термин « руна » нигде не встречается.

Если это кодовая точка, то она должна называться кодовой точкой , вот так просто.

Если бы термин « руна » никогда не появлялся в стандарте, это могло бы быть нормально, проблема в том, что он появляется несколько раз в главе 8 по отношению к рунам. Это не просто неправильно, это активное смешивание одного вопроса с другим.

Просто для меня слово grapheme было бы более самоописывающим.

Если речь идет о 32-битных кодовых точках, термин grapheme может ввести в заблуждение, потому что графема — это снова что-то другое.

Я часто хотел тип данных с кодовой точкой (не так давно, так как то, над чем я работал, изменилось, но несколько лет назад я очень этого хотел и написал перекрывающиеся частичные решения для частей этой потребности и можно было бы сделать с хорошо протестированной библиотекой). Я не понимаю, почему это нельзя назвать чем-то вроде CodePoint . Большинство людей, которые понимают, что им нужен такой тип, скорее всего, в любом случае будут думать с точки зрения кодовых точек, а не с точки зрения рун; или же с точки зрения кодовых точек и рун как отдельных частей их задачи. ᚱᚢᚾᚪ ᛒᛇᚦ ᛥᛁᛚᛖ ᛒᚱᚣᚳᛖᚢ/rúna béoþ Stille bryceu/руны все еще используются. Мне нужно использовать руны примерно раз в год, и обычно с пергаментом и чернилами, а не с чем-то цифровым, но, безусловно, есть люди, которые имеют дело с ними и в цифровом виде. (Даже с данными 20-го века я знаю случай, когда они использовались для архивирования данных эпохи Второй мировой войны).

Графема еще сложнее, так как часто требуется перейти от октетов к символам (которые уже прекрасно обрабатываются .NET), затем к символам → кодовым точкам, а затем к кодовым точкам → графемам.

пометив это как доступное на данный момент.

Следующие шаги : Нам нужно: официальное предложение, которое будет включать отзывы сверху (фактическое наименование типа и преимущества его использования по сравнению с простым использованием Int32).

Я обновил проблему как с предлагаемым API, так и с начальной реализацией:

https://github.com/migueldeicaza/NStack/blob/master/NStack/unicode/Rune.cs

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

@migueldeicaza, прежде чем пометить его как готовый к рассмотрению, что вы думаете по поводу опасений по поводу фактического именования типа, как вы думаете, возможно, CodePoint может быть лучше с точки зрения описания того, что это за тип?

Я думаю, что аргумент в пользу использования кодовой точки в качестве имени слаб.

Использование его — ужасная идея, в долгосрочной перспективе это должно заменить каждое использование «char» в существующем коде — если мы надеемся получить надлежащую поддержку Unicode.

Я бы хотел, чтобы мы использовали «char», как это делает Rust, но, к сожалению, мы уже использовали его, и у нас есть сломанный.

Go, приняв это имя, является хорошим прецедентом.

Я согласен с тем, что термин code point здесь не подходит. По крайней мере, согласно стандарту Unicode, он не включает значения выше 10FFFF (http://unicode.org/glossary/#code_point).

Мне не нравится термин rune . Я думаю, что он уже используется в Unicode и в других местах, что в целом вызовет только путаницу. Я также думаю, что у него есть довольно хорошие шансы конфликтовать с существующими типами пользователей (особенно для таких вещей, как Unity, где «Руна» может представлять определенный игровой объект).

Однако мне нравится идея типа, охватывающего тип C++ 11 char32_t , только с другим именем.

Есть что сказать о Char32 . Это точно, это аналогично именам типов целочисленных типов. Он говорит на концептуальном уровне символов, а не на уровне кодовых точек. Это не имя сценария.

Поскольку мы ищем nint , как насчет nchar ?

Прецедент был бы в базах данных nchar и nvarchar

Где nchar - национальный символ / национальный характер, а nvarchar - национальный символ, изменяющийся / национальный характер; какие типы полей вы можете хранить в юникоде, а также какой-то стандарт ISO - не уверен, какой, может быть, SQL?

Что это за использование руны в Юникоде? Это новость для меня.

от U+16A0 до U+16F8

Он используется для ссылки на определенную кодовую страницу в стандарте Unicode. Это поднималось несколько раз в этой ветке: http://unicode.org/charts/PDF/U16A0.pdf

Ну рунический, не рунический.

Имя резервной копии (System.Rune или System.Char32) не так важно, как метка, которая будет проецироваться в C#.

Во-первых: да, да, и еще об этом, пожалуйста. Мне нравится эта идея (честно говоря, у меня уже давно была похожая идея). На самом деле мы уже некоторое время используем пользовательский класс строк и структуру символов в нашей совместимости с Git в Visual Studio (Git говорит в Utf-8, и транскодирование всего происходит очень медленно).

Что касается имен статических методов, можем ли мы избежать произвольного короткого именования? Учитывая, что Char.IsPunctuation является текущим методом, можем ли мы отразить его с помощью Rune.IsPunctuation или аналогичного?

Предполагая (всегда опасно), что это будет принято, можем ли мы иметь внутреннюю rune или c32 или просто полностью заменить char реализацией System.Rune ?

Я предлагаю unichar или uchar , хотя uchar будет выглядеть как символ без знака. Однако, что бы ни было выбрано, я надеюсь, что мы получим для него псевдоним для конкретного языка. Я лично большой поклонник использования языковых псевдонимов для примитивных типов.

Также я согласен с @whoisj - определенно предпочел бы полные имена методов коротким/аббревиатурам.

Также я согласен с @whoisj - определенно предпочел бы полные имена методов коротким/аббревиатурам.

IMO язык (и его библиотеки) должен выбирать либо полные, сокращенные имена, либо полностью использовать аббревиатуры (например, C с strcmp, memcpy и т. д.)

или просто полностью заменить char реализацией System.Rune ?

Это было бы критическим изменением по довольно очевидным причинам.

Это было бы критическим изменением по довольно очевидным причинам.

Мои комментарии были в основном ироничными, дерзкими и обнадеживающими. 16-битный тип символа был ошибкой с самого начала.

Хороший улов на именовании, исправим.

В предоставленном API есть и другие небольшие несоответствия, мы также рассмотрим их исправление.

@migueldeicaza

Ну рунический, не рунический.

Руническое — прилагательное, руническое — существительное. Все рунические знаки являются рунами.

_Runic_ — прилагательное, _rune_ — существительное. Все рунические знаки являются рунами.

Справедливо, как кажется, «Cortana: определить _'rune'_» предлагает:

буква древнегерманского алфавита, родственная латинскому алфавиту.

Ах да, всякий раз, когда я вижу слово «руна», я сразу же думаю об этой малоизвестной главе о спецификации, которую никто не читал, в которой говорится о «руническом блоке Unicode».

😆 Я вспоминаю детские воспоминания о чтении Толкина.

ᛁ᛫ᚦᛁᛜᚲ᛫ᛟᚠ᛫ᚱᚢᚾᛖᛋ

Да, я не думаю конкретно о спецификации, но я думаю о типах персонажей, к которым относится спецификация.

Вы говорите rune , а я думаю о магии, фэнтези, загадочных головоломках, древних языках и т. д.

Я рад, что вы не видите слово «руна» и сразу думаете: «А, это явно относится к руническому блоку Unicode 7.0, значение которого будет ограничено этими уникальными значениями в диапазоне 16A0..16F8».

Я знаю, что Таннер здесь единственный голос, и некоторые из вас все еще думают: «Но Мигель, я вижу слово «руна» и сразу же думаю о типе данных, который когда-либо мог содержать только 88 возможных значений». Если это проблема, с которой вы боретесь, брат/сестра, у меня для вас новости: у вас есть большие проблемы.

Я слежу за этой темой какое-то время со смесью волнения и нерешительности чуть больше месяца. В прошлом месяце я посетил конференцию по интернационализации и Unicode, и ни одна из презентаций не была посвящена .NET. Существует проблема восприятия .NET Framework; тот, который не обязательно является незаслуженным, учитывая историю его особенностей глобализации. При этом я люблю программировать на C# и очень хочу увидеть новые функции, которые укрепляют место .NET в действительно глобальном сообществе. Я думаю, что это предложение является хорошим шагом в направлении принятия стандартов, которые сообщество интернационалистов ожидает от программного обеспечения.

Моя нерешительность в основном была вызвана спорами о названии типа. Хотя это правда, что разработчики Go выбрали название «руна», это проблематично по причине, неоднократно указанной выше: есть кодовые точки, которые правильно называются рунами. Мне трудно согласиться с предложением, которое пытается приблизиться к уважаемому стандарту, а затем переопределяет терминологию, являющуюся частью спецификации. Кроме того, аргумент о том, что большинство разработчиков не знают этого термина, является благовидным, учитывая, что разработчики, наиболее заинтересованные в правильном использовании этого типа, с большей вероятностью понимают спецификацию Unicode и имеют хорошее представление о том, что такое «руна». Представьте себе странность, которая могла бы возникнуть, если бы вы смешали терминологию:

Rune.IsRune(new Rune('ᛁ')); // evaluates to true
Rune.IsRune(new Rune('I')); // evaluates to false

Конечно, я пошел по легкому пути, критикуя, не называя нового имени. Я думаю, что предыдущее предложение CodePoint является наиболее информативным вариантом (и оно появляется в исходном описании проблемы), но char32 будет иметь большее соответствие с существующими примитивными типами (хотя я бы стесняйтесь сказать, что не каждая кодовая точка является символом). Если цель состоит в улучшении поддержки Unicode в .NET, я полностью поддерживаю этот путь, но лучший способ сделать это — следовать спецификации.

Три предложения:

  1. В классе Rune отсутствует критическое значение IsCombining. Без этого мы не можем преобразовать серию рун (кодовых точек) в серию графем.
  1. Я хотел бы также иметь соответствующий класс Grapheme. Графема в этом контексте на самом деле представляет собой просто список из одной или нескольких рун (кодовых точек), так что первая руна не объединяется, а остальные руны объединяются. Вариант использования, когда разработчику нужно иметь дело с фрагментами «видимых символов». Например, + GRAVE — это две руны, образующие одну графему.

  2. В сети мы часто получаем кусок байтов, который нам нужно превратить в «строковый» объект, где байты могут быть неполными (например, нам сообщают о некоторых байтах, но последний байт в многобайтовой последовательности не имеет значения). т вполне прибыл еще). Я не вижу никакого очевидного способа преобразовать поток байтов в поток рун, чтобы отсутствие последнего байта многобайтовой последовательности считалось нормальной ситуацией, которая будет исправлена, когда мы получим следующий набор байтов.

И, наконец, используйте имена Unicode и назовите их CodePoint. Да, консорциум Unicode делает ужасную работу по объяснению разницы. Но решение состоит в том, чтобы добавить четкую и полезную документацию; все остальное запутывает проблему вместо того, чтобы помочь прояснить ее.

Я не знаю, с чего начать запрос на объединение, ни Go, Rust или Swift не предлагают такой API для rune, Character или Unicode Scalar (их имена для System.Rune ). Пожалуйста, предоставьте предлагаемую реализацию.

На графемных кластерах это хорошая идея, ее нужно отслеживать независимо от System.Rune . Как бы то ни было, Swift использует для этого Character , но Swift также не является отличной моделью для обработки строк.

Превращение потоков байтов в правильную руну — задача, относящаяся к API более высокого уровня. Тем не менее, вы можете посмотреть на мою реализацию ustring , которая использует тот же субстрат, что и моя реализация System.Rune , чтобы увидеть, как эти буферы отображаются в строки utf8:

https://github.com/migueldeicaza/NStack/blob/master/NStack/strings/ustring.cs

Документация, которую я еще не обновлял с тех пор, как ввел в API System.Rune , но охватывает ее:

https://migueldeicaza.github.io/NStack/api/NStack/NStack.ustring.html

Что касается именования, очевидно, что Rust — лучший с char , но мы напортачили с этим. На втором месте Go с rune . Все, что больше четырех символов, будет мешать людям делать правильные вещи.

Мне жаль; Я думаю, что CodePoint — очень хорошее имя. Он не требует пояснений, запоминается и автозаполняется с помощью c p .

IsCombining определенно будет необходимо, но также необходимо знать класс объединения, и как только мы получим, что IsCombining в значительной степени сахар, поскольку это просто IsCombining => CombiningClass != 0 или IsCombining => CombiningClass != CombiningClass.None . Кластеры графем действительно снова будут за его пределами, но отправной точкой будет знание комбинированного класса для кластеризации по умолчанию, изменения порядка и т. д.

CodePoint — отличное имя для типа с кодовыми точками, а четыре символа — вряд ли предел, с которым нам приходится иметь дело с другими активно используемыми типами; string на 50% больше и не мешает нам использовать его регулярно. Четыре случайно выбранные буквы были бы лучшим названием, чем повторение ошибки Го.

Поскольку uint не совместим с CLS, не существует CLS-совместимого ctor, охватывающего астральные планы. Также потребуется int .

Двусторонние неявные преобразования могут привести к проблемам с перегрузками, поэтому одно направление, возможно, должно быть явным. Не понятно какой. С одной стороны, uint / int шире, чем кодовые точки, поскольку значения ниже 0 или выше 10FFFF 16 не имеют смысла, а неявное преобразование позволяет быстрее использовать больше существующих API для числа. С другой стороны, я вижу желание чаще переводить число в кодовую точку, чем наоборот.

Поскольку uint не совместим с CLS, нет совместимого с CLS ctor, охватывающего астральные планы. int тоже был бы необходим.

Это если в общий язык не был введен новый внутренний тип.

JonHanna -- вы имеете в виду, что эти три конструктора:
общедоступный статический неявный оператор uint (Rune rune);
общедоступный статический неявный оператор Rune (char ch);
общедоступный статический неявный оператор Rune (значение uint);

должно быть "int" вместо "uint". AFAICT, int легко покрывает весь набор астральных (не BMP) планов.

@PeterSmithRedmond Я имею в виду, что помимо двух конструкторов, один из которых принимает char , а другой принимает uint , должен быть один, принимающий int , но да, также должен быть int Оператор преобразования implicit и что explicit - другой вопрос). Нет ничего плохого в том, чтобы иметь uint для тех языков, которые могут его использовать; в конце концов, это вполне естественное совпадение.

Если это должно заменить System.Char, должно быть возможно выполнить «арифметику» на нем (то есть ==, !=, >, <не уверен в +, -, *, /) и, что более важно, должна быть поддержка литералов этого типа, например, я должен быть в состоянии написать:

rune r = '𐍈'; // Ostrogothic character chose on purpose as in UTF16 will be a "surrogate pairs"


image

Если не rune , то может работать только другой синоним character , возможно, letter ?

имя существительное

  1. письменное или печатное сообщение, адресованное какому-либо лицу или организации и обычно передаваемое по почте.
  2. символ или символ, который обычно используется в письме и печати для обозначения звука речи и является частью алфавита.
  3. кусок печатного шрифта с таким символом или символом.

Хотя это будет противоречить букве и цифре

Буква имеет даже более точное значение в юникоде (да и в Сети вообще), чем руна.

Я думаю, что если мы собираемся сделать этот символьный тип Unicode, нам нужно следовать соглашениям об именах Unicode; что означает _"кодовая точка"_.

Кодовая точка . (1) Любое значение в кодовом пространстве Unicode; то есть диапазон целых чисел от 0 до 10FFFF16. (См. определение D10 в разделе 3.4 «Символы и кодирование» .) Не все кодовые точки назначаются закодированным символам. См. тип кодовой точки . (2) Значение или позиция символа в любом кодированном наборе символов.

Или, может быть, мы просто сдаемся и называем утку «уткой» и называем их символами Юникода (иначе uchar ).

Почему бы просто не решить эту проблему, используя вместо этого System.CodePoint ?
Имхо, это более правильно с точки зрения терминологии Unicode, и другие люди в мире Java используют его. Итак, вместо того, чтобы иметь собственный термин, давайте придерживаться условий Unicode. Это имеет больше смысла и более универсально с точки зрения общих символов и реализации строк в .NET, также зная тот факт, что String в .NET представляет собой набор символов, и этот набор символов основан на Unicode.

Я знаю, потому что жил и в мире Java, и в мире .NET.
И, возможно, давайте начнем с черновой реализации этого.

На самом деле есть два компонента, и оба потребуются (CodeUnit в https://github.com/dotnet/corefxlab/issues/1799 от @GrabYourPitchforks)

C# keyword      Ugly Long form      Size
----------------------------------------
ubyte      <=>  System.CodeUnit    8 bit  - Assumed Utf8 in absence of encoding param
uchar      <=>  System.CodePoint  32 bit

CodeUnit / ubyte важны для представления кодировки переменной ширины и для использования в Span<ubyte> , чтобы гарантировать, что API-интерфейсы текста доступны для текстовых типов, но не для необработанных байтов.

CodePoint / uchar важен для разумной обработки; например, .IndexOf(❤) как ubyte сам по себе не может использоваться для поиска многобайтового символа Unicode; и перечисление более ubyte s было бы чревато опасностью, поэтому нумератор должен работать в единицах uchar .

Объединив два предложения, получится что-то вроде

using System;
using System.Runtime.InteropServices;

// C# Keywords
using ubyte = System.CodeUnit;
using uchar = System.CodePoint;
using uspan = System.Utf8Span;
using ustring = System.Utf8String;

namespace System
{
    public ref struct Utf8Span
    {
        private readonly ReadOnlySpan<ubyte> _buffer;

        public Utf8Span(ReadOnlySpan<ubyte> span) => _buffer = span;
        public Utf8Span(uspan span) => _buffer = span._buffer;
        public Utf8Span(ustring str) => _buffer = ((uspan)str)._buffer;
        public Utf8Span(ReadOnlyMemory<ubyte> memory) => _buffer = memory.Span;

        // Returns the CodeUnit index, not CodePoint index
        public int IndexOf(char value) => IndexOf(value, 0);
        public int IndexOf(char value, int startIndex) => IndexOf(value, 0, _buffer.Length);
        public int IndexOf(char value, int startIndex, int count);
        public int IndexOf(char value, StringComparison comparisonType);

        public int IndexOf(uchar value) => IndexOf(value, 0);
        public int IndexOf(uchar value, int startIndex) => IndexOf(value, 0, _buffer.Length);
        public int IndexOf(uchar value, int startIndex, int count);
        public int IndexOf(uchar value, StringComparison comparisonType);

        public uspan Substring(int codeUnitIndex);
        public uspan Substring(int codeUnitIndex, int codePointCount);

        public bool StartsWith(uchar ch) => _buffer.Length >= 1 && _buffer[0] == ch;
        public bool StartsWith(ustring str) => StartsWith((uspan)str);
        public bool StartsWith(uspan value) => _buffer.StartsWith(value._buffer);
        public bool EndsWith(uchar ch) => _buffer.Length >= 1 && _buffer[0] == ch;
        public bool EndsWith(ustring str) => EndsWith((uspan)str);
        public bool EndsWith(uspan value) => _buffer.EndsWith(value._buffer);

        public Enumerator GetEnumerator() => new Enumerator(this);

        // Iterates in uchar steps, not ubyte steps
        public ref struct Enumerator
        {
            public Enumerator(uspan span);

            public uchar Current;
            public bool MoveNext();
            public void Dispose() { }
            public void Reset() => throw new NotSupportedException();
        }
    }

    public class Utf8String
    {
        private readonly ReadOnlyMemory<ubyte> _buffer;

        public Utf8String(ustring str) => _buffer = str._buffer;
        public Utf8String(ReadOnlyMemory<ubyte> memory) => _buffer = memory;

        public bool StartsWith(uchar ch) => ((uspan)this).StartsWith(ch);
        public bool StartsWith(ustring value) => ((uspan)this).StartsWith(value);
        public bool StartsWith(uspan value) => ((uspan)this).StartsWith(value);
        public bool EndsWith(uchar ch) => ((uspan)this).EndsWith(ch);
        public bool EndsWith(ustring value) => ((uspan)this).EndsWith(value);
        public bool EndsWith(uspan value) => ((uspan)this).EndsWith(value);

        public static implicit operator uspan(ustring value) => new uspan(value._buffer);

        // Returns the CodeUnit index, not CodePoint index
        public int IndexOf(char value) => IndexOf(value, 0);
        public int IndexOf(char value, int startIndex) => IndexOf(value, 0, _buffer.Length);
        public int IndexOf(char value, int startIndex, int count);
        public int IndexOf(char value, StringComparison comparisonType);

        public int IndexOf(uchar value) => IndexOf(value, 0);
        public int IndexOf(uchar value, int startIndex) => IndexOf(value, 0, _buffer.Length);
        public int IndexOf(uchar value, int startIndex, int count);
        public int IndexOf(uchar value, StringComparison comparisonType);

        public ustring Substring(int codeUnitIndex);
        public ustring Substring(int codeUnitIndex, int codePointCount);

        public uspan.Enumerator GetEnumerator() => ((uspan)this).GetEnumerator();
    }

    [StructLayout(LayoutKind.Auto, Size = 1)]
    public struct CodeUnit : IComparable<ubyte>, IEquatable<ubyte>
    {
        private readonly byte _value;

        public CodeUnit(ubyte other) => _value = other._value;
        public CodeUnit(byte b) => _value = b;

        public static bool operator ==(ubyte a, ubyte b) => a._value == b._value;
        public static bool operator !=(ubyte a, ubyte b) => a._value != b._value;
        public static bool operator <(ubyte a, ubyte b) => a._value < b._value;
        public static bool operator <=(ubyte a, ubyte b) => a._value <= b._value;
        public static bool operator >(ubyte a, ubyte b) => a._value > b._value;
        public static bool operator >=(ubyte a, ubyte b) => a._value >= b._value;

        public static implicit operator byte(ubyte value) => value._value;
        public static explicit operator ubyte(byte value) => new ubyte(value);

        // other implicit conversions go here
        // if intrinsic then casts can be properly checked or unchecked

        public int CompareTo(ubyte other) => _value.CompareTo(other._value);

        public override bool Equals(object other) => (other is ubyte cu) && (this == cu);

        public bool Equals(ubyte other) => (this == other);

        public override int GetHashCode() => _value;

        public override string ToString() => _value.ToString();
    }

    [StructLayout(LayoutKind.Auto, Size = 4)]
    public struct CodePoint : IComparable<uchar>, IEquatable<uchar>
    {
        private readonly uint _value;

        public CodePoint(uint CodePoint);
        public CodePoint(char ch);

        public static ValueTuple<uchar, int> DecodeLastCodePoint(ubyte[] buffer, int end);
        public static ValueTuple<uchar, int> DecodeLastCodePoint(ustring str, int end);
        public static ValueTuple<uchar, int> DecodeCodePoint(ubyte[] buffer, int start, int n);
        public static ValueTuple<uchar, int> DecodeCodePoint(ustring str, int start, int n);
        public static int EncodeCodePoint(uchar CodePoint, ubyte[] dest, int offset);
        public static bool FullCodePoint(ubyte[] p);
        public static bool FullCodePoint(ustring str);
        public static int InvalidIndex(ubyte[] buffer);
        public static int InvalidIndex(ustring str);
        public static bool IsControl(uchar CodePoint);
        public static bool IsDigit(uchar CodePoint);
        public static bool IsGraphic(uchar CodePoint);
        public static bool IsLetter(uchar CodePoint);
        public static bool IsLower(uchar CodePoint);
        public static bool IsMark(uchar CodePoint);
        public static bool IsNumber(uchar CodePoint);
        public static bool IsPrint(uchar CodePoint);
        public static bool IsPunctuation(uchar CodePoint);
        public static bool IsSpace(uchar CodePoint);
        public static bool IsSymbol(uchar CodePoint);
        public static bool IsTitle(uchar CodePoint);
        public static bool IsUpper(uchar CodePoint);
        public static int CodePointCount(ubyte[] buffer, int offset, int count);
        public static int CodePointCount(ustring str);
        public static int CodePointLen(uchar CodePoint);
        public static uchar SimpleFold(uchar CodePoint);
        public static uchar To(Case toCase, uchar CodePoint);
        public static uchar ToLower(uchar CodePoint);
        public static uchar ToTitle(uchar CodePoint);
        public static uchar ToUpper(uchar CodePoint);
        public static bool Valid(ubyte[] buffer);
        public static bool Valid(ustring str);
        public static bool ValidCodePoint(uchar CodePoint);

        public static bool operator ==(uchar a, uchar b) => a._value == b._value;
        public static bool operator !=(uchar a, uchar b) => a._value != b._value;
        public static bool operator <(uchar a, uchar b) => a._value < b._value;
        public static bool operator <=(uchar a, uchar b) => a._value <= b._value;
        public static bool operator >(uchar a, uchar b) => a._value > b._value;
        public static bool operator >=(uchar a, uchar b) => a._value >= b._value;

        // etc
    }
}

Я использовал UnicodeScalar в своих реализациях прототипа для ссылки на скалярное значение Unicode (значения в диапазоне U+0000..U+10FFFF включительно; исключая суррогатные кодовые точки) и Utf8Char для обозначения кодовой единицы UTF-8. Похоже, что многие люди предпочитают _Rune_ вместо _UnicodeScalar_, потому что это менее сложно. Мне все равно, но я укажу, что термин «скалярное значение Unicode» — это тот же термин, который используется в спецификации Unicode . ;)

В .NET Framework также есть концепция «текстового элемента», который представляет собой один или несколько скаляров, которые при объединении создают единую неделимую графему. Подробнее об этом в MSDN . В частности, когда вы перечисляете строку, вы можете захотеть перечислить кодовую единицу ( Utf8Char или Char ), скалярное значение ( UnicodeScalar ) или текстовый элемент, в зависимости от вашего конкретный сценарий. В идеале мы должны поддерживать все три типа как в String, так и в Utf8String.

Поверхность API для нашего прототипа не закончена и может быть быстро изменена, но вы можете увидеть некоторые текущие мысли на https://github.com/dotnet/corefxlab/tree/utf8string/src/System.Text.Utf8/System . https://github.com/dotnet/corefxlab/blob/master/src/System.Text.Primitives/System/Text/Encoders/Utf8Utility.cs.

Немного не по теме:
Должен ли «текстовый элемент» быть сегментацией, определенной «Границами кластера графемы» в UAX dotnet/corefx#29 ?

using System;
using System.Globalization;

class Program
{
    static void Main()
    {
        var e = StringInfo.GetTextElementEnumerator("👩🏻‍👦🏼👨🏽‍👦🏾‍👦🏿👩🏼‍👨🏽‍👦🏼‍👧🏽👩🏻‍👩🏿‍👧🏼‍👧🏾");
        while (e.MoveNext())
        {
            Console.WriteLine(e.GetTextElement());
        }
    }
}

ожидаемый результат:
👩🏻‍👦🏼
👨🏽‍👦🏾‍👦🏿
👩🏼‍👨🏽‍👦🏼‍👧🏽
👩🏻‍👩🏿‍👧🏼‍👧🏾

фактический результат:
👩
🏻

👦
🏼
👨
🏽

👦
🏾

👦
🏿
👩
🏼

👨
🏽

👦
🏼

👧
🏽
👩
🏻

👩
🏿

👧
🏼

👧
🏾

UnicodeScalar по-прежнему очень легко набирать. u s c Space (автозаполнение) Поскольку это правильный, наиболее понятный термин, я очень надеюсь, что мы его получим.

@ufcpp Это хороший момент. Не стесняйтесь открывать новую тему для этого. Если мы не можем изменить поведение по причинам совместимости, я предлагаю отказаться от этого типа и создать перечислитель графем, соответствующий спецификации.

ubyte / uchar сбивают с толку. Они читаются как unsigned char / unsigned byte , учитывая соглашение, установленное с ushort / uint / ulong . Возможно, char8 / u8char и char32 / u32char понятнее?

В любом случае, я думаю, что мы не согласны с тем, являются ли кодовые единицы и кодовые точки UTF-8:

  1. низкоуровневые примитивные типы данных в .NET — например, byte , int
  2. формат данных для преобразования в/из существующих примитивов - например, DateTime , Guid

И затем, как мы представляем API, связанные с кодовыми точками, учитывая это решение?

Вариант 1 означает обработку текста с помощью примитивов char8, char16 и char32 (и сопутствующих u8string, u16string и u32string), как в C++17. Тогда char32 как rune — это плохое имя, учитывая, что у нас уже есть char16 как char , и нам также нужно третье имя для char8 .

Вариант 2 означает, что byte и int/uint «достаточно хороши» для хранения кодовых единиц и кодовых точек UTF. Это означает, что все строки остаются в кодировке UTF-16. CodePoint / rune решает проблемы семантики Code Point , а не бинарного представления , и не предназначен для ввода-вывода .

IMO UTF-8/UTF-32 - это просто форматы данных (вариант 2). Рассматривайте их как данные (byte/int). Для меня CodePoint больше похож DateTime или Guid (другой идентификатор*), чем int — не низкоуровневый примитивный тип, напрямую не поддерживаемый в IO (т.е. BinaryWriter), нет необходимости во встроенных функциях.

@miyu Прототип, который мы приводим в corefxlab, ближе к варианту 1. Существуют определенные типы данных для представления единиц кода, и эти типы данных предназначены для внутреннего представления текстовых данных и не могут использоваться для передачи текстовых данных по сети. (Как вы заметили, .NET уже сегодня работает так: System.Char — это кодовая единица строки UTF-16, но System.Char нельзя отправить по сети.)

Кроме того, существуют API для преобразования между byte[] / Span<byte> / и т. д. (это двоичное представление всех данных, подходящее для ввода-вывода) и примитивными типами, такими как Utf8String / String / Guid / и т. д. Некоторые из них более просты, чем другие. Например, мы можем выставить удобное свойство Utf8String.Bytes , которое возвращает ReadOnlySpan<byte> для использования в вводе/выводе, и этот метод получения свойства может иметь сложность O(1). Мы бы не стали вводить такое свойство для типа String , хотя вы можете себе представить удобный метод String.ToUtf8Bytes() . И даже если бы существовало свойство Utf8String.Bytes , элементарный тип прямого перечисления экземпляра Utf8String не был бы byte . Это будет Utf8CodeUnit (название подлежит уточнению) или UnicodeScalar , в зависимости от того, что, по нашему мнению, имеет больше смысла для тех типов приложений, которые разработчики хотят создавать.

Глупая необычная идея — как насчет wchar (_wide char_)? Сегодня большинство компиляторов C и C++ (за исключением Windows) уже используют wchar_t для представления функционального эквивалента 32-разрядной единицы кода. Заметным исключением является Windows, где wchar_t определяется как 16-разрядный тип, но разработчики, которые сегодня p/invoke в Windows уже должны знать о различиях в разрядности между .NET char и char в стиле C.

Тип / ключевое слово wchar нарушило бы наши соглашения об именах, но мы просто выбрасываем это на рассмотрение.

Глупая необычная идея - как насчет wchar (широкий символ)?

Работает на меня

Тип/ключевое слово wchar нарушило бы наши соглашения об именах,...

Не похоже, что мы собираемся получить короткое ключевое слово языка C#

https://github.com/dotnet/apireviews/pull/64#discussion_r196962756 кажется крайне маловероятным, что мы будем вводить ключевые слова языка для этих типов, поскольку они должны быть контекстными (т.е. в зависимости от того, могут ли они разрешаться в тип с имя ключевого слова, которое им все равно придется связывать с этим типом, а не с типом, представленным ключевым словом).

Итак, если мы хотим чего-то приятного... т.е. NotLotsOfCapitalFullWords ...

Хотя мне обычно нравятся соглашения об именах .NET, длинное имя немного оскорбительно для по существу int , которое также, вероятно, будет использоваться в дженериках и в качестве переменных цикла.

например никто не делает

foreach (Int32 i in list)
{
    // ...
}

Они? (Конечно...)

foreach (UnicodeScalar us in str)
{
    // ...
}

Гораздо хуже

foreach (wchar c in str)
{
    // ...
}

Вроде нормально...

rune , wchar и uchar (предложено в другом потоке) все звучит хорошо для меня. Любые предложения для пэра string ? wstring , ustring или другое?

... и почему бы не получить ключевое слово языка C#? Конечно, отсутствие его в первом выпуске имеет смысл, но если в будущем обработка строк не будет иметь ключевого слова, это будет не только неискренним, но и откровенно враждебным по отношению к его принятию.

/CC @MadsTorgersen @jaredpar

почему бы не получить ключевое слово языка С#?

Новые ключевые слова являются критическими изменениями в 100% случаев. Независимо от того, какое слово вы выберете, есть компания, у которой есть тип этого имени, которое используется везде в их проекте. Единственный вариант, который у нас есть, — это контекстные ключевые слова: например, var .

У меня смешанные чувства по поводу использования контекстного ключевого слова для этого. Ключевые слова существующего типа ( int , string и т. д.) имеют конкретное преимущество перед фактическим именем типа ( Int32 , String ):

  • string : относится к типу System.String в сборке, которую компилятор идентифицирует как corelib. С этим именем связана нулевая двусмысленность.
  • String : компилятор не понимает этот тип. Это такой же тип, как и любой другой, и он проходит через все те же правила поиска, что и типы, которые вы определяете. Это может быть эквивалентно string , а может и не быть.

Как только мы введем здесь контекстные ключевые слова, тогда rune может быть:

  • Тип System.Rune внутри сборки corelib
  • Тип rune , который вы определили два года назад, когда читали о Go .

Поиск rune столь же неоднозначен, как и String , поэтому я не вижу твердого преимущества в использовании его в качестве контекстного ключевого слова.

Кстати: вот почему вы должны использовать string , а не String 😄

Кстати: вот почему вы должны использовать string , а не String

Это 99% причин, по которым я думаю, что люди хотят ключевое слово языка. Другой 1% — это просто «выглядит лучше» 😏

Большой палец вниз за сильную неприязнь к ключевому слову "руны".

Лучшее слово — глиф, так как оно уже представляет общую концепцию элементарного символа в типографике.

Руна — это особый тип глифа, который по иронии судьбы определен Юникодом. Ссылаться на Go как на предшествующий уровень техники несколько нелепо. Предшествующее искусство рун — это то, что было написано еще в 150 году нашей эры, и настоящие физические рунические камни. Не то, что кто-то в Редмонде считает руной. Подобные попытки переопределить существующие концепции необычны, поскольку .NET обычно имеет хорошо спроектированную поверхность API. Это редкое исключение очень плохого именования API, и я хочу высказать свое недовольство.

Лучшее слово — глиф, так как оно уже представляет общую концепцию элементарного символа в типографике.

Проблема заключается в том, что термин «Глиф» используется при отображении юникода в видимый текст (от: utf8everywhere.org ).

Глиф

Особая форма внутри шрифта. Шрифты — это наборы глифов, разработанные шрифтовым дизайнером. Механизм формирования текста и рендеринга отвечает за преобразование последовательности кодовых точек в последовательность глифов в указанном шрифте. Правила такого преобразования могут быть сложными, зависеть от локали и выходить за рамки стандарта Unicode.

Ссылаться на Go как на предшествующий уровень техники несколько нелепо.

Использование термина Роба Пайка и Кена Томпсона, использованного при создании Utf-8 https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt

Роб Пайк сейчас работает над Go, поэтому он использует оригинальный термин.

Руна — это особый тип глифа, который по иронии судьбы определен Юникодом.

Рунический код определяется Unicode, а рунический нет.

Рунический код определяется Unicode, а рунический нет.

Я не думаю, что это точное утверждение, в последней спецификации юникода (http://www.unicode.org/versions/Unicode11.0.0/UnicodeStandard-11.0.pdf) есть 37 совпадений для «руны» (только 36 действительны). , последний является частью более крупного слова) и всегда используется для обозначения отдельных букв рунического алфавита.

Я не думаю, что это точное утверждение, последняя спецификация юникода содержит 37 совпадений для «рун».

В основном тексте с описанием мотивов; ни в имени персонажа, ни в имени текстового блока (где его рунический и рунический символы)

В основном тексте с описанием мотивов; ни в имени персонажа, ни в имени текстового блока (где его рунический и рунический символы)

Хорошо, честно. Но затем мы возвращаемся к вопросу о том, что текущая спецификация Unicode не определяет термин «руна», а когда он используется, то для информативного текста, описывающего «рунические символы».

Что формально определяет и использует для описания вещей, так это «Кодовая точка» и «Кодовая единица».

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

Должен быть коротким, иначе его использование становится уродливым

int CountCommas(string str)
{
    int i = 0;
    foreach(UnicodeCodePoint c in str.AsUnicodeCodePoints())
    {
        if (c == ',') i++;
    }
}

string Trim(string str)
{
    int end = str.Length - 1;
    int start = 0;

    for (start = 0; start < Length; start++)
    {
        if (!UnicodeCodePoint.IsWhiteSpace(str.GetUnicodeCodePointAt(start)))
        {
            break;
        }
    }

    for (end = Length - 1; end >= start; end--)
    {
        if (!UnicodeCodePoint.IsWhiteSpace(str.GetUnicodeCodePointAt(start)))
        {
            break;
        }
    }

    return str.SubString(start, end);
}

против

int CountCommas(string str)
{
    int i = 0;
    foreach(Rune c in str.AsRunes())
    {
        if (c == ',') i++;
    }
}

string Trim(string str)
{
    int end = str.Length - 1;
    int start = 0;

    for (start = 0; start < Length; start++)
    {
        if (!Rune.IsWhiteSpace(str.GetRuneAt(start)))
        {
            break;
        }
    }

    for (end = Length - 1; end >= start; end--)
    {
        if (!Rune.IsWhiteSpace(str.GetRuneAt(start)))
        {
            break;
        }
    }

    return str.SubString(start, end);
}

Что касается длины, я бы полностью выбрал CodePoint.IsWhiteSpace и str.GetCodePointAt , но Rune тоже забавно, и я не возражаю против этого.

@ jnm2 Мы бы не использовали GetCodePointAt , когда дело доходит до строк. Это слишком двусмысленно: мы не знаем, нужна ли вам char , оказавшаяся по этому индексу (поскольку все char s — даже непарные суррогаты — также являются допустимыми кодовыми точками) или скаляр / руна, оказавшаяся под этим индексом.

@GrabYourPitchforks Может ли GetRuneAt избежать той же проблемы, или вы говорите, что ни то, ни другое не имеет смысла?

@jnm2 jnm2 Я просто сказал, что CodePoint , в частности, слишком неоднозначно в этом сценарии. В противном случае имя метода GetXyzAt должно совпадать с именем типа Xyz , которое в конечном итоге входит.

К вашему сведению, основная реализация теперь проверена (см. https://github.com/dotnet/coreclr/pull/20935). Дайте ему некоторое время для распространения на corefx, после чего API-интерфейсы ref поступят через https://github.com/dotnet/corefx/pull/33395. Вы можете оставить этот вопрос открытым или решить его по своему усмотрению.

Я не рассчитываю ни на кого повлиять или смогу что-то изменить, просто для протокола:

Лучшее слово — глиф, так как оно уже представляет общую концепцию элементарного символа в типографике.

Проблема заключается в том, что термин «Глиф» используется при отображении юникода в видимый текст (от: utf8everywhere.org ).

Эта линия рассуждений также не поддерживает руны, потому что термин «руна» использовался более тысячи лет на протяжении всей истории, задолго до того, как Unicode, транзисторы, Microsoft или открытый исходный код когда-либо существовали. По крайней мере, это указывает на то, что некоторые произвольно применяют разные стандарты к разным предложениям, что явно непоследовательно, так что, возможно, это больше о том, кто был первым или громче всех, а не о самом последовательном аргументе, откуда я знаю. Я просто опоздал, пытаясь понять процесс, но это не имеет смысла.

Ссылаться на Go как на предшествующий уровень техники несколько нелепо.

Использование термина Роба Пайка и Кена Томпсона, использованного при создании Utf-8 https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt

Роб Пайк сейчас работает над Go, поэтому он использует оригинальный термин.

Го и Роб Пайк относительно новички в этой теме. На самом деле их мнение несколько неуместно с точки зрения определения того, что такое руна исторически, в популярной литературе и обществе. Роб сам не кувал рунические камни вручную, поэтому у него мало квалификации, чтобы определить, что такое руна. Бьюсь об заклад, он даже не может написать или прочитать рунический сценарий, но это мое предположение. В лучшем случае он может уловить эту концепцию с помощью кодирования, но он не может прийти и сказать, что китайский иероглиф, арабское письмо, хангыль или смайлик — это руна или что-то еще, что является «кодовой точкой», теперь также является руной. или что-то подобное. Это почти неуважительно попирает термин, смотрите, теперь все может быть руной, а это означает, что руны - это не что иное, как четырехбуквенный подстановочный знак, обозначающий что-то эзотерическое в области кодирования текста.

Руна — это особый тип глифа, который по иронии судьбы определен Юникодом.

Рунический код определяется Unicode, а рунический нет.

Юникод не должен переопределять, что такое руна или руна. Если они это сделают, то превысят свои полномочия. Им нечего рассказывать публике, что такое руны. На самом деле им нет дела до определения какого-либо нового языка или системы знаков. Они не могут просто присвоить слово, которое уже является явно перегруженным термином за тысячу лет, а затем бегать вокруг, аплодируя, как будто они изобрели новое понятие. Руническое письмо состоит только из рун, а руны — уже устоявшееся понятие. Если вы спросите случайного человека на улице, что такое руна, он не подумает о Unicode.

Вдобавок ко всем вышеперечисленным проблемам, руны — плохая метафора, и это самое худшее. Это ничего не проясняет. Это просто добавляет еще один уровень путаницы. Любой новичок в этой теме теперь должен пройти раунд объяснения и чтения значений неоднозначности, потому что каждый приходит с контекстом, что руна — это историческая система письма, используемая в определенных культурах. Объяснение должно быть примерно таким: «Руна — это кодовая точка Unicode». «Но почему бы не назвать это кодовой точкой?» «Ну, потому что это слишком долго.», или «Кто-то решил, что им нравятся руны». В общем, потому что кто-то думает, что 9 букв слишком много по сравнению с 4 (хотя у них есть автодополнение с Intellisense и ничто по сравнению с Java Kingdom Of Nouns), теперь мы должны разобраться с этой путаницей и объяснить это тысячам. разработчиков, которым, возможно, придется побаловаться с Unicode. Просто используйте оператор using, чтобы сократить термин, если вы часто используете его в коде.

Это не обязательно должен быть UnicodeCodePoint, это может быть просто CodePoint. Это уже уникально. Есть много терминов API, которые длиннее, чем «CodePoint», поэтому этого должно быть достаточно. Если все еще слишком долго, просто используйте оператор using с некоторой аббревиатурой.

Я предвижу, что это станет одним из тех вопросов на собеседовании, которые на самом деле не добавляют особой ценности или не имеют логической основы для чего-либо полезного. По крайней мере, для метафоры «веха», хотя мы говорим о символических словах, используемых в разработке программного обеспечения на основе понятий, происходящих от камня и скалы, веха имеет реальное описательное значение. Он сразу передает концепцию, с которой все знакомы. Ага, веха, как когда ты в дальнем пути и проходишь мимо по тропе. Это хорошая метафора реального мира, которая действительно помогает визуализировать что-то и может мгновенно стать управленческим языком. Я не могу представить, чтобы люди говорили о рунах таким образом, если они не были хорошо знакомы с этой темой, и в этот момент они уже знали, что это просто уловка для кодовой точки.

Лучшее слово — глиф, так как оно уже представляет общую концепцию элементарного символа в типографике.

Проблема заключается в том, что термин «Глиф» используется при отображении юникода в видимый текст (от: utf8everywhere.org).

Эта линия рассуждений также не поддерживает руны, потому что термин «руна» использовался более тысячи лет на протяжении всей истории, задолго до того, как Unicode, транзисторы, Microsoft или открытый исходный код когда-либо существовали.

Моя точка зрения заключалась в том, что слово «глиф» проблематично, поскольку оно уже использовалось как одно из понятий при рендеринге текста; это графическое представление этого символа в определенном шрифте. Таким образом, персонаж может быть представлен множеством различных глифов.

... снова с @benaadams , имеющим 10 000-метровый взгляд на вещи и правильный ответ 😁

Честно говоря, нам придется жить со старой поговоркой: «Вы можете сделать некоторых людей счастливыми все время и всех людей какое-то время; но вы не можете сделать всех людей счастливыми навсегда». время." Это очень похоже на прежнюю ситуацию.

Сигил?

Exit, pursued by a bear.

Как человек, который будет широко использовать этот API, я решительно голосую за кодовую точку. Терминология Unicode уже достаточно запутана, и несоответствий уже предостаточно. Вы сделаете мою жизнь намного проще, если я буду везде говорить «кодовая точка».

Я сейчас лежу в постели. Если я повернусь боком, я увижу доску, прислоненную к моей стене. В течение нескольких месяцев эта доска была домом для различных каракулей и диаграмм, пока я пытался понять, как эффективно работать с IDN в C#. Я отношусь к нему как к реликвии, которую вызвал из глубин ада. Если бы я попытался объяснить описанную логику, я бы не смог.

Пожалуйста, не усложняйте мне жизнь. Кодовая точка — это кодовая точка. Это не руна, не глиф, иероглиф, графема и даже не символ. Он не должен представлять ничего значимого для человека — это может быть управляющий код. Она может не представлять собой визуальный символ, как следует из названия «руна». Это просто кодовая точка.

Более конкретный аргумент заключается в том, что «руна» подразумевает представление одной графемы, что очень часто не так. Если я подсчитаю количество кодовых точек и количество графем, я могу получить два очень разных числа. Одна и та же последовательность графем может быть представлена ​​двумя различными сериями кодовых точек.

Лучшее слово — глиф, так как оно уже представляет общую концепцию элементарного символа в типографике.

Это еще хуже. Одна кодовая точка может быть представлена ​​несколькими глифами, а один глиф может представлять несколько кодовых точек. Точное сопоставление может варьироваться в зависимости от системы, программы, шрифта...

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

Просто как пример того, как сложно может быть иметь дело с текстом, даже на таком распространенном языке, как немецкий:

  1. Преобразуйте ß в верхний регистр, и вы получите SS .
  2. Преобразуйте его обратно в нижний регистр, и вы получите ss .

Проблемы:

  • Что должно вернуть char.ToUpper('ß') ? (Он должен вернуть один символ.)
  • В Unicode 5.1 добавлена ​​заглавная буква ß, которую мой телефон не может ввести в это текстовое поле. Если я попытаюсь вставить его, я получаю SS. Теперь конверсия выше/ниже стала еще более неоднозначной.
  • Изменение оболочки струны меняет ее длину.
  • Изменения регистра не являются идемпотентными или обратимыми.
  • Вы не можете выполнить сравнение без учета регистра, просто переводя каждую строку в нижний регистр.

Несмотря на то, что это не прямой пример ситуации, в которой терминология вызывает проблемы, он демонстрирует, что существуют пограничные случаи, о которых мы обычно не задумываемся. Придание каждому термину отдельного, последовательного значения помогает программистам сообщать об этих проблемах. Если я попрошу товарища по команде написать функцию для подсчета графем, он точно знает, что он будет считать и как это сделать. Если я попрошу их посчитать кодовые точки, опять же, они точно знают, что делать. Эти определения не зависят от языков и технологий, которые мы используем.

Если я попрошу разработчика JavaScript посчитать руны, они посмотрят на меня так, будто у меня три головы.

Википедия говорит

Unicode определяет кодовое пространство из 1 114 112 кодовых точек в диапазоне от 0hex до 10FFFFhex.

Кодовая точка кажется официальным названием. Я прочитал эту ветку и не нашел убедительного аргумента, почему кодовая точка была бы неправильной.

Я согласен, что кодовая точка не является правильным термином для использования здесь. По крайней мере, согласно стандарту Unicode, он не включает значения выше 10FFFF (http://unicode.org/glossary/#code_point).

Может быть, эта фраза просто неверна? Он говорит «любое значение в кодовом пространстве». Таким образом, это явно означает все, в то же время неправильно получая целое число.

Кроме того, «руна» имеет значение в реальном мире , которое не имеет ничего общего с Unicode. В Германии слово «руна» имеет нацистский оттенок, потому что руны имеют «германскую» историю, на которую нацисты любили ссылаться.

Я нахожу "руна" сбивающим с толку названием. Кто-нибудь здесь действительно любит «руну» или аргументы за нее основаны на правильности. Интуитивно это очень плохое имя.

Может быть, эта фраза просто неверна? Он говорит «любое значение в кодовом пространстве». Таким образом, это явно означает все, в то же время неправильно получая целое число.

Это предложение верно. Кодовое пространство от U+0000 до U+10FFFF. Теоретически Unicode можно будет когда-нибудь расширить, но это нарушит UTF-8 и UTF-16. Нам понадобятся новые кодировки.

Редактировать: На самом деле, не цитируйте меня по поводу поломки UTF-16, но я почти уверен, что это сломает UTF-8. UTF-8 определенно не может представлять 0xFFFFFF (2 ^ 24 -1).

Редактировать 2: Чтобы уточнить, Unicode утверждает, что кодовые точки никогда не могут превышать U + 10FFFF. Это не означает, что в настоящее время существует 0x110000 кодовых точек — большинство из этих кодовых точек не назначены.

@Zenexer @GSPP

Этот тип, зарегистрированный в настоящее время в master ( System.Text.Rune ), очень точно соответствует «скалярному значению Unicode» ( см. глоссарий ). Ctors типа вызовут исключение, если вы попытаетесь создать его из значений -1 , 0xD800 или 0x110000 , поскольку они не являются скалярными значениями в соответствии со спецификацией Unicode. Если вы принимаете параметр Rune в качестве входных данных для своего метода, вам не нужно выполнять для него какую-либо проверку. Система типов уже гарантирует, что она была построена из допустимого скалярного значения.

Re: преобразование регистра. Все API преобразования регистра в .NET Framework, если не указано иное, используют технику, называемую простым складыванием регистра. В соответствии с правилами простого свертывания регистра для любого входного скалярного значения гарантируется, что выходные формы нижнего регистра, верхнего регистра и заглавного регистра будут ровно одним скалярным значением. (Некоторые входные данные, такие как цифры 0-9 или знаки пунктуации, не имеют записей в карте преобразования регистра. В этих случаях операции, такие как _ToUpper_, просто возвращают входное скалярное значение.) Кроме того, согласно простым правилам свертывания регистра, если входные данные в Basic Multilingual Plane (BMP), то вывод тоже должен быть в BMP; и если вход находится в дополнительной плоскости, выход также должен быть в дополнительной плоскости.

У этого есть некоторые последствия. Во-первых, Rune.ToUpper и друзья всегда будут возвращать одно значение _Rune_ (скалярное). Во-вторых, String.ToUpper и друзья всегда будут возвращать строку той же длины, что и ее вход. Это означает, что строка, содержащая «ß» (минимальный eszett), после операции преобразования регистра может содержать «ß» (без изменений) или «ẞ» (majuscule eszett), в зависимости от используемого языка и региональных параметров. Но он _не будет_ содержать "SS", потому что это изменило бы длину строки, и почти все общедоступные API преобразования регистра .NET используют простые правила свертывания регистра. В-третьих, Utf8String.ToUpper и друзья (еще не зарегистрировавшиеся) _не_ гарантированно возвращают значение, свойство _Length_ которого соответствует свойству _Length_ входного значения. (Количество единиц кода UTF-16 в строке не может измениться после простого свертывания регистра, но количество единиц кода UTF-8 в строке может измениться. Это связано с тем, как значения BMP кодируются UTF-16 и UTF- 8.)

Существуют некоторые API-интерфейсы .NET, которые внутренне используют сложные правила свертывания регистра, а не простые правила свертывания регистра. String.Equals , String.IndexOf , String.Contains и подобные операции используют сложные правила складывания регистра под обложками, в зависимости от языка и региональных параметров. Поэтому, если ваш язык и региональные параметры установлены на _de-DE_, односимвольная строка «ß» и двухсимвольная строка «SS» будут сравниваться как равные, если вы передадите _CurrentCultureIgnoreCase_.

@GrabYourPitchforks Я в первую очередь возражаю против выбора имени. Пример со складыванием регистра был сделан исключительно для того, чтобы подчеркнуть, насколько сложным может быть Unicode (и текст в целом). Пока есть какой-то способ справиться с normalization , меня не слишком волнует, как работают простые операции, так как я все равно буду конвертировать в NFKD для всего в моем случае использования.

Это предложение верно. Кодовое пространство от U+0000 до U+10FFFF. Теоретически Unicode можно будет когда-нибудь расширить, но это нарушит UTF-8 и UTF-16. Нам понадобятся новые кодировки.

Просто для придирки (или, если кому-то интересно): теоретически алгоритм UTF-8 работает до 42 бит (префиксный байт 0xFF и 7 байтов 6-битной полезной нагрузки), и изначально первые спецификации охватывали полные 31 бит.

Для UTF-16 ситуация сложнее. Но они могли зарезервировать кодовые точки в верхней плоскости как «Escapes» для 32-битных или более битов. Поскольку уровни с 3 по 13 в настоящее время не определены, они могут зарезервировать два из них как «низкий суррогатный уровень» и «высокий суррогатный уровень». Затем 32-битная кодовая точка будет разделена на два 16-битных значения (по одному в каждой плоскости), а затем каждое значение будет закодировано с использованием двух «классических» суррогатов, эффективно используя 4 кодовых единицы по 16 бит каждая для кодирования 32-битной кодовой точки.

Кстати, AFAICS, консорциум Unicode публично заявил, что они никогда не будут выделять кодовые точки выше U + 10FFFF, поэтому на практике я надеюсь, что уйду на пенсию задолго до того, как это действительно произойдет. :подмигивание:

Этот тип, который в настоящее время зарегистрирован для мастера ( System.Text.Rune ), очень точно сопоставляется со «скалярным значением Unicode».

@GrabYourPitchforks спасибо за это разъяснение. Это означает, что структура не представляет кодовую точку. Так что это имя действительно было бы неправильным.

Думаю, имя UnicodeScalar слишком загадочное...

@GrabYourPitchforks , что осталось сделать с этой проблемой?

@stephentoub Для встроенного типа Rune в версии 3.0 не запланировано никаких дополнительных функций, но у @migueldeicaza были идеи по расширению охвата этого типа, в том числе для таких вещей, как кластеры графем. (Самое близкое, что у нас есть, это TextElementEnumerator , это очень устаревший тип.) Некоторые из этих идей обсуждались в этой ветке, но пока нет ничего конкретного.

Мы могли бы оставить эту проблему открытой на случай, если сообщество захочет продолжить обсуждение сценариев, или мы могли бы предложить людям открывать новые проблемы, если они хотят внести конкретные предложения. TBH У меня нет сильных предпочтений.

Спасибо. Поскольку Rune уже был представлен, а описанные здесь API-интерфейсы (или их приближения) уже раскрыты, давайте закроем это. Дополнительная поддержка может быть решена через отдельные вопросы.

Так это по существу стабилизировалось на данный момент? Потому что, честно говоря, это ужасное имя, которое не согласуется ни с какой информацией о Unicode, которую вы найдете в хороших и точных источниках, и имеет неприятный нюанс, заключающийся в том, что оно подразумевает глиф, а не непечатаемый символ, только собирается ухудшить и без того ужасное понимание Unicode вашим средним программистом.

Я знаю, что это было интегрировано к этому моменту, но я просто хочу присоединиться к части Rune и некоторым разногласиям людей по поводу названия.

Впервые я столкнулся с Rune в Plan 9 и, как и другие, видел его в Go и других. Когда msdocs начал перечислять Rune , я точно знал, что это было, прежде чем читать.

Как минимум в двух случаях, Plan 9 и Go, у вас есть лица, ответственные за UTF-8, использующие имя Rune . Я думаю, можно с уверенностью сказать, что они уже думали об этих опасениях и по-прежнему считали Rune разумными. Руническая система письма больше не используется, за исключением некоторых традиционалистов. И Rune действительно означает графему в этой системе, точно так же, как это по существу означает графему здесь (за исключением таких случаев, как управляющие символы.

Я действительно не вижу ничего плохого в названии. Руническая система - такая старая система письма, что я очень сомневаюсь, что ваш средний программист спутает ее, и уже несколько десятилетий де-факто существует стандарт Rune для правильных «символов Unicode».

@энтомия

точно так же, как это по существу означает графему здесь (за исключением случаев, таких как управляющие символы.

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

И наоборот, даже когда одна кодовая точка представляет одну графему, они очень часто объединяются в _кластер графем_, что необходимо для правильной обработки текста в большинстве индийских языков. Таким образом, один символ, воспринимаемый пользователем при перемещении с помощью клавиш со стрелками, часто соответствует нескольким последовательным кодовым точкам. Таким образом, не может быть простого соответствия между кодовыми точками и графемами или кластерами графем. Даже «персонаж», вероятно, было бы лучшим названием, учитывая, что программисты привыкли считать персонажей странными и дурацкими на данный момент, в то время как «руна» создает впечатление, что проблема определения воспринимаемых пользователем границ символов была решена для программиста. уже тогда, когда его на самом деле не было.

Когда msdocs начал перечислять Rune, я точно знал, что это было, прежде чем читать.

Тот факт, что вы думали, что название «руна» хорошо описывает графемы, является очень хорошим доказательством проблемы, с которой я здесь столкнулся: название «руна» дает программистам ложное чувство безопасности, облегчая предположение о наличии такого соответствия.

Как минимум в двух случаях, Plan 9 и Go, у вас есть лица, ответственные за UTF-8, использующие имя Rune .

При всем моем уважении к Кену Томпсону и Робу Пайку, их работа сводилась к разработке очень умной схемы кодирования последовательности целых чисел переменной длины. Они не являются экспертами по Unicode в целом, и я совершенно не согласен с ними в этом вопросе. Я признаю, что я тоже не эксперт по Unicode, но я не думаю, что апелляция к авторитету здесь так сильна, как может показаться.

и уже существует де-факто стандарт Rune, которому несколько десятилетий, для правильных «символов» Unicode.

"Стандарт" говоришь? В основном это были только эти двое, которые продвигали название, и несколько второстепенных языков программирования, таких как Nim, переняли его у Go. И, конечно же, я должен еще раз повторить, что кодовая точка не представляет ни одного «правильного символа Unicode», будь то в смысле выбора, перемещения клавиш со стрелками, графем или кластеров графем.

...по сути, здесь имеется в виду графема...

Да, как бы не совсем так, но примерно достаточно близко. Графемы, по крайней мере, как они определены в лингвистике, являются орфографическими компонентами, которые составляют систему письма и используются для выражения фонем. Это не 1:1 вещь. В слоговых и логосиллабариях одна графема может представлять несколько фонем, обычно пару согласных и гласных. И наоборот, в алфавитных языках часто встречаются случаи, когда несколько графем представляют одну фонему, например, «th» в английском языке отвечает за архаичные eth и thorn, в зависимости от конкретного слова. Тогда вы даже не сможете найти согласие между языками относительно того, является ли такая буква, как «а», собственной уникальной буквой или «а» с ударением. Мы даже не можем установить согласованность языков, которым более тысячи лет. У нас не будет идеально согласованного дополнения к этому, то есть их кодирования.

Поскольку вы выступаете за чрезвычайно строгую семантику, то, что UNICODE называет «кластером графем», в лингвистике часто является всего лишь одной графемой. Это недействительный UNICODE? Нет. Означает ли это, что UNICODE нужно переименовать? Нет почему? Потому что контекст. У полей есть свой жаргон, и пока в одном поле нет объединения, это не проблема.

Я не считаю имя слишком важным. Msdocs ясно дает понять, что такое Rune в сводке. Если люди не читают документы, это их собственные проблемы. Люди не реагируют яростно на «Ручей» и не говорят ерунды вроде «а что, если люди думают, что это маленькая река, потому что у нее уже есть такое же название!» Нет.

@Serentty @Entomy Вас обоих также может заинтересовать класс StringInfo , который раскрывает актуальную концепцию Unicode «расширенные кластеры графем». Тип StringInfo довольно древний и в результате реализует очень старую версию стандарта Unicode, но ведется активная работа по его обновлению для обеспечения совместимости с UAX #29, Sec.

Да, как бы не совсем так, но примерно достаточно близко.

Я думаю, что вопрос о составленных и разложенных представлениях делает это неверным. Если мы исходим из лингвистического определения графемы, а не из определения, связанного с вычислительной техникой, то 한 и 한 представляют собой одну и ту же последовательность графем (три джамо хангыль, представляющие слог _han_ в виде сегментов HAN), и однако первый представляет собой только одну кодовую точку, тогда как второй представляет собой последовательность из трех.

У полей есть свой жаргон, и пока в одном поле нет объединения, это не проблема.

Это и моя точка зрения. Unicode — действительно сложная система со своей собственной терминологией, так зачем пытаться навязывать ей какой-то непродуманный «интуитивный» термин, если он не соответствует точно? Кодовые точки — это кодовые точки. У них нет лингвистической параллели, и попытка сделать их интуитивно понятными при точности всего 75% — это рецепт такой же катастрофы, от которой C# все еще пытается оправиться.

Поскольку вы выступаете за чрезвычайно строгую семантику, то, что UNICODE называет «кластером графем», в лингвистике часто является всего лишь одной графемой.

В стандарте допускается, что кластер может состоять только из одной графемы. В этом нет ничего плохого. _cluster_ — это единица выделения текста и перемещения курсора.

Я не считаю имя слишком важным. Msdocs ясно дает понять, что такое Rune в сводке. Если люди не читают документы, это их собственные проблемы.

Это аргумент «программисты должны быть умнее», который постоянно приводится в защиту плохих дизайнерских решений. Если программистам все равно нужно читать документацию и узнавать, что руна — это кодовая точка Unicode, то какой смысл вообще называть ее более «интуитивным» именем? Аргумент здесь, по-видимому, заключается в том, что «кодовая точка» сбивает с толку, поэтому имеет смысл выбрать более интуитивно понятное имя, но затем, столкнувшись с проблемой, что имя вводит в заблуждение, защита заключается в том, что программисты все равно должны знать, что такое кодовая точка. от чтения документации. Если это так, то почему бы просто не назвать тип CodePoint и облегчить программистам поиск и изучение? Все это оставляет в стороне вопрос о том, что документация .NET довольно ужасна в отношении Unicode, в первую очередь, рассматривает суррогатные пары как запоздалую мысль в мире «16-битных символов Unicode».

Это аргумент «программисты должны быть умнее», который постоянно приводится в защиту плохих дизайнерских решений.

Я никогда этого не говорил.

Аргумент здесь, похоже, заключается в том, что «кодовая точка» сбивает с толку.

Я тоже этого никогда не говорил.

Люди не реагируют яростно на «Ручей» и не говорят ерунды вроде «а что, если люди думают, что это маленькая река, потому что у нее уже есть такое же название!» Нет.

Я говорю, что программисты достаточно умны, чтобы не думать, что Rune — это именно руническая руна, примерно так же, как они знают, что Stream — это не маленькая река.

Позвольте мне повторить это

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

Я не считаю имя слишком важным. Msdocs ясно дает понять, что такое Rune в сводке. Если люди не читают документы, это их собственные проблемы.

Это то, что я имею в виду здесь. Аргумент в пользу названия «руна» основан на интуиции и интуитивной связи с понятием графемы. Вы сами утверждали, что эти двое выстроились достаточно близко, чтобы это не было проблемой. Когда я указал на все варианты того, что эта интуиция была ошибочной и соответствие могло быть очень плохим, вы, по сути, ответили, что это не имеет значения, потому что программистам все равно нужно читать документацию. Вот что я имею в виду под фразой «программисты должны быть умнее». Документация не является оправданием для вводящих в заблуждение имен, если для них нет устаревшей причины.

Я говорю, что программисты достаточно умны, чтобы не думать, что Rune — это именно руническая руна, примерно так же, как они знают, что Stream — это не маленькая река.

Мой аргумент здесь не в том, что люди будут путать его с руническими рунами. Мой аргумент состоит в том, что люди будут путать его с глифами, графемами и кластерами графем, которые, несмотря на вашу настойчивость, очень плохо коррелируют с кодовыми точками.

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

Достаточно умны, чтобы понять, что это не настоящие германские руны, конечно. Но понять, что это не глифы, графемы или кластеры графем? Мой реальный опыт работы с качеством большинства программ, обрабатывающих Unicode, говорит, что нет.

Если люди не читают документы, это их собственные проблемы.

Да, и я стою на этом. Не из-за недостатка интеллекта, а скорее из-за склонности к поспешным предположениям.

Если программист предполагает, что String означает прочный, тонкий кусок веревки, сделанный из скручивания нитей, потому что да, это означает, что это не считается проблемой с именем String .

Если программист предполагает, что Char означает обугленный материал, такой как древесный уголь или определенный вид форели, это не считается проблемой с названием Char .

Если программист предполагает, что character означает отображение набора ментальных и этических черт, используемых в повествовании, это не считается проблемой с названием character .

Обратите внимание, что это все текстовые/лингвистические вопросы. Все они имеют другие значения. И все же программисты прекрасно акклиматизировались. Эти термины стали стандартами де-факто из-за устоявшегося соглашения в этой области: нашего жаргона. Существует установленный прецедент, что программисты _являются_ достаточно умными, чтобы следовать этому.

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

Да, это GitHub. По уже закрытой проблеме, где я просто добавлял свои мысли о том, почему я чувствовал, что Rune подходит, потому что в названии был какой-то установленный прецедент. Здесь не место и не контекст для написания трактата, наполненного обширными определениями и тщательно подобранными словами. Например, если я помещаю PR, скажем, для декодера UTF-8, я не буду подробно описывать, почему я применил DFA Hoehrmann вместо альтернативных подходов. Я просто скажу: «Вот оно, вот некоторые доказательства того, что это работает, вот несколько тестов, подтверждающих, почему я пошел с этим».

Я утверждаю, что люди будут путать его с глифами, графемами и кластерами графем.

Они не путают ничего из вышеперечисленного, ни Tree , Heap , Table , Key , Socket , Port ...

Это крайне неискренний аргумент. Кусок нити и строку текста нелегко спутать. Структуру данных высокого растения и дерева нелегко спутать. С другой стороны, кодовая точка является очень плохо понятой концепцией для большинства программистов, и ее постоянно путают со всеми другими концепциями, которые мы обсуждали. Решение этой проблемы, как вы говорите, — чтение документации. Однако язык, использующий собственное «умное» имя для кодовых точек, еще больше затрудняет применение знаний из _фактической документации Unicode_ к этому языку. И это подводит меня к следующему:

Эти термины стали стандартами де-факто из-за устоявшегося соглашения в этой области: нашего жаргона.

И в этом суть всего. Кажется, вы утверждаете, что либо «руна» — это устоявшийся термин для кодовой точки, широко понимаемой в программировании, либо так и должно быть. Если первое, то я приглашаю вас спросить среднестатистического программиста, имеющего опыт работы с основным языком программирования, кроме Go, слышали ли они его когда-нибудь. Если последнее, то я хотел бы спросить вас, какой смысл конкурировать с официальной терминологией Unicode в и без того запутанной и плохо понятой ситуации, которую часто неправильно понимают даже очень опытные разработчики.

@Entomy вход со стороны: весь ваш аргумент, насколько я могу судить, заключается в том, что «это запутанно и плохо, да, но это не так уж запутанно и плохо».
Так? Почему это не может быть на самом деле хорошо вместо этого? В чем проблема назвать его именно так, как его называет Unicode?
Кроме того, руны не являются кодовыми точками или даже графемами или кластерами в общей области вычислений. Если вы ищете «руны Unicode» в Google, ничего, что связывает их с кодовыми точками, не появляется до страницы 2, и даже тогда это просто ссылки на godoc / Nim. Даже на DuckDuckGo, с которым программистам может быть удобнее, это все еще результат страницы 2. Таким образом, единственный аргумент в пользу имени, которое я видел, заключается в том, что интуитивно понятно, что оно представляет собой кодовую точку, но это не так. Интуитивно понятно, что он представляет кластер графем или, возможно, просто графему.
Источник: я использовал Go и думал, что это графема, пока четыре года спустя я не прочитал этот выпуск только сейчас.

(и говоря, что это нормально, что это предлагает графему, потому что она «достаточно близка», напоминает мне о том, что 16-битный символ достаточно близок.)
Да, если бы программисты были умнее и читали больше документации, нам не понадобилось бы осмысленное имя или даже тип. Люди просто знали бы, что нужно передавать кодовые точки в int вокруг вместо char. Но это не так. Они так же умны, как и сейчас, и это не изменится только потому, что был добавлен Yet Another API. Цель состоит в том, чтобы увеличить количество программного обеспечения, которое правильно обрабатывает языки, отличные от английского, а не просто ввести новые способы делать то же самое и сохранить те же барьеры для входа, что и раньше.

Просто в качестве аргумента и в научных целях я хотел бы указать всем здесь на один язык программирования, который лучше всего обрабатывает текст Unicode, где «лучший» определяется как «ближайший в соответствии со стандартом Unicode», а не притворяясь простотой: Swift

  • String — это буфер произвольного текста Unicode.
  • Character , который вы повторяете, а что нет, не является отдельным скалярным значением Unicode, а расширенным кластером графемы. См. этот пример для графемного кластера : let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
  • Если вам нужны скалярные значения Unicode, вы также можете перебирать их. Их тип называется UnicodeScalar .
  • И если вы действительно чувствуете, что вам это нужно, вы также можете перебирать кодовые единицы UTF-8 и UTF-16, получая UInt 8 s и UInt 16 s.

Теперь я не предлагаю, чтобы C# полностью соответствовал стилю Swift. Хотя это было бы потрясающе, это также требует чертовски много изменений и работы. Однако я здесь, чтобы предложить подобрать именование в стиле Swift по всем причинам , указанным @Serentty , и оставить возможность открытой, чтобы в конечном итоге превратить текстовые строки в стиль Swift.

Некоторые потенциально лучшие имена, чем Rune : CodeUnit32 , UnicodeScalar , CodeUnit , UniScalar , UnicodeValue , UniValue , UnicodeScalarValue . Я думаю, что первые два могут хорошо вписаться в соглашения об именах C#. Обратите внимание, что UnicodeScalar объективно является лучшим названием, поскольку кодовые единицы — это просто способы кодирования скалярного значения Unicode в жаргоне Unicode. Таким образом, CodeUnit32 подразумевает перебор единиц кода текстовой строки в кодировке UTF-32, тогда как UnicodeScalar не зависит от кодировки.

Редактировать: Да, имя System.Rune уже существует. Все это просто «если мы хотим сделать это лучше, прежде чем этой штуке исполнится полвека».

@пирог-вкус

весь ваш аргумент, насколько я могу судить, звучит так: «да, это запутанно и плохо, но не так уж запутанно и плохо».

Нет, это совсем не мой аргумент. Я делаю все возможное с моей инвалидностью, но это не мое предполагаемое общение.

Если вы ищете «руны Unicode» в Google, ничего, что связывает их с кодовыми точками, не появляется до страницы 2, и даже тогда это просто ссылки на godoc / Nim.

Если вы выполните поиск «строка Unicode» в Google, вы также не узнаете, как именно работают строки .NET. Это вопрос поиска соседней вещи. Если провести очень строгую аналогию, я программирую и на .NET, и на Аде; string не одно и то же между ними, и небольшое чтение для каждого из них является хорошей идеей.

Перегруженные определения не являются чем-то необычным для языка, и все же мы прекрасно справляемся. Это может вас удивить, но у слова «бег» есть по крайней мере 179 формальных определений, у слова «взять» по крайней мере 127, у слова «брейк» есть по крайней мере «123» и так далее. [ источник ] Люди удивительно способны и могут успешно справляться с гораздо более сложными задачами, чем то, что здесь считается проблематичным. На мой взгляд, озабоченность тем, что «руна» имеет по крайней мере 2 формальных определения, не оправдана, когда можно показать, что люди справляются с более чем 50-кратными перегрузками.

Кроме того, это грубо эксплуатирует поведение поисковых систем. В большинстве поисковых систем вы получаете результаты в зависимости от того, сколько страниц ссылаются на что-либо. Есть и другие факторы, каждый из которых взвешивает вещи по-разному. Поскольку .NET Rune — относительно новая концепция, по сравнению с ней, будет гораздо меньше контента, рассказывающего о ней, и потребуется больше страниц, чтобы добраться до нее. Но он также использует неправильный инструмент поиска. Если я хочу найти исследования по алгоритмам поиска строк, чтобы узнать, появилось ли что-то новое за последние несколько лет, я не ищу Google или DDG. Semantic Scholar, Google Scholar и другие — лучшие отправные точки. Точно так же, если вы хотите разобраться в .NET API, вы сначала ищете в MSDocs. Если я жалуюсь, что «момент инерции», физический/технический термин, имеет расплывчатое или вводящее в заблуждение название, и его следует переименовать, потому что я не могу найти никакой информации о нем в первых нескольких книгах, начиная с наименьшего числа в библиотеке, использующей десятичную классификацию Дьюи, это не проблема с названием «момент инерции»; Я явно ищу не в том месте.

Источник: я использовал Go и думал, что это графема, пока четыре года спустя я не прочитал этот выпуск только сейчас.

Я просмотрел документы Go и примечания к выпуску, по крайней мере, те, которые смог найти, и должен с вами согласиться. Они очень расплывчаты в отношении того, что такое rune , и, к сожалению, даже неясны в отношении того, насколько велик rune . Я подозреваю, что эта неопределенность вызовет проблемы позже, поскольку я видел, как Ада столь же расплывчата в отношении ограничений типов данных, и годы спустя она кусала себя за задницу.

Однако я должен сказать, что msdocs работает намного лучше с очень подробным и кратким описанием.

Представляет скалярное значение Юникода ([ U+0000..U+D7FF ] включительно или [ U+E000..U+10FFFF ] включительно).

При этом замечаний несколько не хватает, и некоторые уточнения о том, почему существует Rune и когда вы захотите его использовать, были бы полезны (а также подходящее место для более подробного объяснения, чем мое упрощенное вышеупомянутое) . Я выдвину некоторые улучшения там.

@Эври

Просто ради аргумента и в научных целях я хотел бы указать всем здесь на один язык программирования, который лучше всего обрабатывает текст Unicode.

Это мнение. С одним я абсолютно согласен; Swift, безусловно, лучше обрабатывает современный UNICODE. Но без цитирования рецензируемых воспроизводимых исследований, подтверждающих эти результаты, это не является научным утверждением.

Теперь я не предлагаю, чтобы C# полностью соответствовал стилю Swift. Хотя это было бы потрясающе, это также требует чертовски много изменений и работы.

И сломает существующее программное обеспечение.

оставьте опцию открытой, чтобы в конечном итоге превратить текстовые строки в стиль Swift.

И сломает существующее программное обеспечение.

Да, название System.Rune уже существует. Все это просто «если мы хотим сделать это лучше, прежде чем этой штуке исполнится полвека».

И сломает существующее программное обеспечение.

В качестве гипотетического варианта, если в существующее имя должны быть внесены изменения, как вы предлагаете существующее программное обеспечение, ориентированное на .NET Core 3.0/3.1, где Rune уже используется, по-прежнему быть совместимым, а также иметь его как другое имя в более поздних целевых средах выполнения?

И сломает существующее программное обеспечение.

Как уже упоминалось, я просто рассуждаю с точки зрения принципа и идеализма. Реальность вещей неоднократно упоминалась. Хотя во всем этом есть нюанс:

  • Использование строк в стиле Swift не обязательно приводит к поломке программного обеспечения. Это просто вопрос добавления дополнительных методов перечисления и типов поверх уже существующего интерфейса String . Я не имею в виду радикальные вещи, такие как изменение System.Char на тип кластера графем или что-то в этом роде.
  • Если бы существующее имя типа, такое как System.Char , было бы переназначено для другого типа, тогда да, это было бы огромным переломным изменением. И безответственное изменение в этом. Я с тобой там.
  • Гипотетический .NET Core 4.0, говорящий на языке SemVer, может делать все, что захочет. Кроме этого, изменения до гипотетической версии 4.0 не так страшны: превратите System.Rune в псевдоним устаревшего типа для System.UnicodeScalar или какое бы там ни было имя. Программное обеспечение, использующее Rune , не заметит разницы, за исключением примечания об устаревании, и новое программное обеспечение может использовать фактический тип с более удачным названием. А гипотетическая версия 4.0 просто сбрасывает Rune .
  • Точно так же System.Char можно превратить в псевдоним для System.CodeUnit16 или что-то в этом роде.
  • Выполнение этого в стиле Swift означает просто добавление System.GraphemeCluster в смесь.
  • Введение большего количества новых псевдонимов ключевых слов для всех этих типов может быть проблематичным.

Просто подбрасываю сюда пищу для размышлений. Я думаю, что System.Rune , хотя имя типа является плохим для своей цели, на самом деле не ухудшает предыдущий статус-кво именования. Я думаю, это здорово, что наконец -то появился правильный тип, способный кодировать все скаляры Unicode. Однако я вижу прекрасную возможность распространить тенденцию к более точной обработке и именованию Unicode. Возможность, которую каждый здесь может отложить в сторону.

Привет всем! Имя System.Text.Rune — это то, что было отправлено и что мы будем использовать в дальнейшем. Ранее было серьезное (и жаркое!) обсуждение использования имени UnicodeScalar вместо Rune , но в конце концов Rune победило. В настоящее время команда не рассматривает идею выбора для него другого имени. И хотя я знаю, что люди увлечены этим, и мы продолжим следить за разговором здесь, в конечном счете знайте, что любая энергия, потраченная на продолжение судебного разбирательства по вопросу об именах, не принесет дивидендов.

Для пояснения и согласно документации: тип System.Text.Rune в .NET точно эквивалентен скалярному значению Unicode. Это обеспечивается конструкцией. Это делает его более похожим на тип Swift UnicodeScalar , чем на тип Go rune .

В настоящее время предпринимаются усилия по добавлению раздела в документы Rune с подробным описанием его вариантов использования и его связи с другими API-интерфейсами обработки текста в .NET и концепциями в Unicode. Проблема с отслеживанием находится по адресу https://github.com/dotnet/docs/issues/15845. Там также есть ссылка из этой проблемы с отслеживанием на текущий проект концептуальных документов.

Для меня основным недостатком UnicodeScalar является большое несоответствие между длиной имени типа и размером данных типа. По сути, это int с некоторыми пробелами в домене.

Однако многословие в использовании было бы экстремальным:

foreach (UnicodeScalar unicodeScalar in name.EnumerateUnicodeScalars())
{
     // ... unicodeScalar contains 1 int
}

против эквивалентного char над string (и в идеале люди должны использовать новый тип над char , поскольку они представляют собой целые значения, а не содержат разделенные значения)

foreach (char c in name)
{
     // ... c contains 1 ushort
}

Rune — это компромисс в многословии имени типа:

foreach (Rune rune in name.EnumerateRunes())
{
     // ... rune contains 1 int
}

@GrabYourPitchforks

Привет! Честно говоря, я втянулся в этот спор не потому, что пытаюсь убедить .NET-людей в том, что название нужно менять, так как кажется, что этот корабль уплыл, а просто потому, что хотел высказать свое мнение другие в этой теме, которые не согласны с этим. Я думаю, это замечательно, что C# наконец-то имеет _real_ символьный тип, в отличие от сломанного символьного типа, который был у него так долго, и имя полностью вторично по отношению к этому. Я понимаю, что необходимо соблюдать огромный баланс между краткостью и точностью, и хотя я бы поместил золотую середину где-то около CodePoint , я понимаю, почему другие не согласятся.

Но еще раз хочу поблагодарить вас за всю тяжелую работу по модернизации поддержки Unicode в .NET! Это то, что имеет огромное значение для многих людей во всем мире.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

iCodeWebApps picture iCodeWebApps  ·  3Комментарии

omariom picture omariom  ·  3Комментарии

bencz picture bencz  ·  3Комментарии

matty-hall picture matty-hall  ·  3Комментарии

noahfalk picture noahfalk  ·  3Комментарии