На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: RaD
  
    > обработка кодовых точек, включая суррогатные пары
      Здравствуйте!

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

      Я в курсе как устроен unicode, кодировки, что такое codepoint, codeunit и так далее.

      О функциях ord и chr написано, что они работают с кодовыми точками. По факту спотыкаются на символах выходящих из базового плана.

      Функция len YНекоторые символы считает двумя, то есть по факту считает codeunit.

      Функция list, аналогично разбивает строку на codeunit.

      Оператор for итерирует, опять же, по codeunit, разрывая суррагатные пары.

      Вроде бы были функции toCodepoints и fromCodepoints. А может это в каком-то другом языке.

      Что-то не могу найти подходящих функций или методов для преобразования.
        Суррогатная пара это два символа, поэтому естественно и рассматривается как два символа. И подсчитывается, как два символа.

        Не понял, как они вообще у тебя в строках возникают, используются они в текстах, кодированных в utf-16, а декодер 'utf-16' при чтении автоматически объединяет их в один символ

        Но вопрос показался интересным
        На просторах интернета довольно быстро нашёлся способ преобразовать строку с суррогатными парами в строку, где они объединены в нормальные символы UNICODE
        ExpandedWrap disabled
          s.encode('utf-16', 'surrogatepass').decode('utf-16')
        Проверил, у меня сработало.

        Кстати, существуют ещё символы с отдельно прописанными диактрическими знаками (YouTube в заголовках такие любит выдавать). Они, считаясь одним символом, тоже считаются за несколько.
          Цитата amk @
          Суррогатная пара это два символа, поэтому естественно и рассматривается как два символа. И подсчитывается, как два символа.

          Вообще-то суррогатная пара - это как раз один символ. Но значение его codepoint больше 65535 и поэтому не влезает в 16 бит. Поэтому в кодировке utf-16 его разделяют на два codeunit. Тогда как в кодировке utf-32 он хранится как есть.

          Цитата amk @
          Не понял, как они вообще у тебя в строках возникают, используются они в текстах, кодированных в utf-16, а декодер 'utf-16' при чтении автоматически объединяет их в один символ

          Захардкодил для теста прямо в строку некоторые эмодзи. Ничего не кодировал. Разве что пробовал строку в двойных кавычках, а затем добавил префикс u.
          ExpandedWrap disabled
            s="🐱"


          Цитата amk @
          На просторах интернета довольно быстро нашёлся способ преобразовать строку с суррогатными парами в строку, где они объединены в нормальные символы UNICODE

          О, как интересно. Тоже встречал похожие варианты, но в прошлый раз отмахнулся. Они показались мне какими-то не по теме. Да и сейчас, откровенно говоря, не понял, в чём прикол.
          Я с питоном знаком недостаточно глубоко. Читал, что там строки представлены в utf-8. А по факту они ведут себя как utf-16. В идеале мне бы хотелось получить строки как utf-32.

          Цитата amk @
          Кстати, существуют ещё символы с отдельно прописанными диактрическими знаками. Они, считаясь одним символом, тоже считаются за несколько.

          Тут немного сложнее.
          Для примера можно рассмотреть ситуацию со знаком ударения, акцента, грависа или умляута. Они - отдельные символы. Тогда буква и диакретический знак - это два символа.
          При этом есть символы скомбирированные сразу с этими знаками. В таком случае - это один символ. Тому примеры наши буква "ё" и "й".
          Там вообще-то ряд таких дополнительных символов. Какие-нибудь подчёркивания и зачёркивания. Есть ещё нижние точки, ударения и так далее.
          https://habr.com/ru/articles/262679/

          Если комбинировать... То, полагаю, можно впихнуть в одно знакоместо сразу несколько символов.
          С выделением диакритики помогает формы нармолизации.
          https://habr.com/ru/articles/579868/
            Цитата Eric-S @
            Да и сейчас, откровенно говоря, не понял, в чём прикол.
            Прикол в том, что строка представленная в памяти как utf-16 сперва перекодируется в байтовую строку, представляющую это самое utf-16. При этом суррогаты кодируются как есть. Потом байтовое представление utf-16 декодируется в строку Python, с объединение суррогатов в символы UNICODE.
            Цитата Eric-S @
            Читал, что там строки представлены в utf-8.
            Тебя обманули, или ты неправильно понял. Строки в Python представляются исключительно в UNICODE. В UTF-8 по умолчанию кодируется исходный текст программы. Но когда ты в текстовом редакторе вставляешь в текст суррогатную пару, не все редакторы это понимают, и могут закодировать её как два многобайтных символа UTF-8. А некоторые вообще в реальности работают не с UNICODE, а именно с UTF-16. В UTF-8 используется схема, позволяющая закодировать все 32 бита широкого символа, поэтому там суррогаты не используются и, соответственно при декодировании не проверяются.

            В версиях 2.x строки были ASCII, и был тип UNICODE-строк.
            В версии 3.0 строки поменяли на UNICODE, объединив эти два типа строк, а для байт-ориентироанных операций над старыми строками ввели тип bytes. Который поддерживает и все старые операции над ASCII строками.
            Начиная с какой-то версии, для экономии памяти при обработке строк тип скрытно разделили на три подтипа: строки ASCII (точнее Latin-1, ASCII это коды до 0x7F), содержащие только символы набора Latin-1 (8 бит на символ), строки UCS-2 для строк, содержащих символы с кодом до 0xFFFF (16 бит на символ), и полноценные строки UNICODE (UCS-4 - 32 бита на символ). Выбор ширины символа производится автоматически, по содержимому. В большинстве случаев хватает 2-байтного представления, а часто и однобайтового.

            Многие европейские языки имеют символы с диактрическими знаками, и для них, как правило, есть отдельный символ в UNICODE.
            Библиотечной функции для объединения таких комбинаций в одну букву не знаю. Можно конечно всё сделать заменой, но когда вариантов много, это может оказаться медленно.
            Может оказаться быстрее пройтись по строке регуляркым выражением, с подменой найденных комбинаций по словарю.
            Сообщение отредактировано: amk -
              Цитата
              Тебя обманули, или ты неправильно понял.

              Предполагаю отсутствие компетенции автора статьи. Что-то частенько натыкаюсь на всякие такие моменты. Поэтому сомневаюсь, ищу подтверждение и проверяю.

              Цитата
              не все редакторы это понимают, и могут закодировать её как два многобайтных символа UTF-8.

              О! Вот даже как! не встречал такого. Тогда, в эту же кучу, можно допустить отсутствие или кривую нормализацию текста.

              Цитата
              некоторые вообще в реальности работают не с UNICODE, а именно с UTF-16. В UTF-8 используется схема, позволяющая закодировать все 32 бита широкого символа, поэтому там суррогаты не используются

              Я в курсе принципов кодирования utf-32, utf-16 и utf-8.

              Цитата
              Начиная с какой-то версии, для экономии памяти при обработке строк тип скрытно разделили на три подтипа: строки ASCII, строки UCS-2 для строк, содержащих символы с кодом до 0xFFFF), и полноценные строки UNICODE (UCS-4 - 32 бита на символ).

              Ничего себе! Огорчает, что скрытно. Хотелось бы это контролировать и управлять. Мне для задачи хорошо подходит UCS-4. Так понял, ваш пример выше, делает именно это.

              Цитата
              Библиотечной функции для объединения таких комбинаций в одну букву не знаю.

              Для нормализации строк Unicode в Python используется функция normalize() из модуля unicodedata. Она принимает в качестве первого аргумента форму нормализации, а в качестве второго — нормализуемую строку.  

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

              У меня была несколько иная задача. Скорость обработки некритична — главное результат.
              Хотел пройтись конечными автоматами, разбить текст на токены, но споткнулся о суррогатные пары.

              Пожалуй, Python не слишком пригоден для моей задачи. Так что взял Java. Там, кстати, сходу нашёлся метод String.codepoints, возвращающую массив кодовых точек.
                Цитата Eric-S @
                Огорчает, что скрытно. Хотелось бы это контролировать и управлять.
                С точки зрения программиста строки кодируются исключительно в UCS-4. Описанное подразделение используется только как недорогой с точки зрения затрат времени способ сжатия информации. Фактически, если старшие биты всех символов строки забиты нулями, то эти нули в памяти не хранятся (для Latin-1 это тоже работает, так как Latin-1 это нулевая страница UNICODE)
                Цитата Eric-S @
                Пожалуй, Python не слишком пригоден для моей задачи.
                Разве что с точки зрения скорости. Так то Python позволяет встроенными средствами решить практически любую задачу, которую в принципе можно решить программно. А чего не может встроенными средствами, для того обычно можно найти пакет на PyPI (правда это непросто, там ежедневно появляется до сотни новых пакетов для решения самых разных задач)
                  Цитата amk @
                  С точки зрения программиста строки кодируются исключительно в UCS-4.

                  Это хорошо, но когда предварительно тестировал, наблюдал поведение характерное для UTF-16. В итоге искал решение проблемы совсем не там. Поэтому создал данную тему.

                  Цитата
                  Разве что с точки зрения скорости. Так то Python позволяет встроенными средствами решить практически любую задачу, которую в принципе можно решить программно. А чего не может встроенными средствами, для того обычно можно найти пакет на PyPI.

                  Как писал выше, скорость не критична. Главная проблема, что я не всегда понимаю логику питона. Ожидаю одно, а получаю что-то не совсем то. Тогда как документация вызывает ещё больше вопросов. В итоге, вместо разработки, раскапываю способы реализации.
                  1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                  0 пользователей:


                  Рейтинг@Mail.ru
                  [ Script execution time: 0,0319 ]   [ 14 queries used ]   [ Generated: 1.06.25, 18:52 GMT ]