Сканирование номеров Unicode в строке с \d

Согласно документации Oniguruma, тип символа \d соответствует:

десятичная цифра char
Unicode: General_Category -- Decimal_Number

Однако сканирование \d в строке со всеми символами Decimal_Number приводит к совпадению только латинских цифр 0-9:

#encoding: utf-8
require 'open-uri'
html = open("http://www.fileformat.info/info/unicode/category/Nd/list.htm").read
digits = html.scan(/U\+([\da-f]{4})/i).flatten.map{ |s| s.to_i(16) }.pack('U*')

puts digits.encoding, digits
#=> UTF-8
#=> 0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९০১২৩৪৫৬৭৮৯੦੧੨…

p RUBY_DESCRIPTION, digits.scan(/\d/)
#=> "ruby 1.9.2p180 (2011-02-18) [i386-mingw32]"
#=> ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

Я неправильно читаю документацию? Почему \d не соответствует другим цифрам Юникода и/или есть ли способ сделать это?


person Phrogz    schedule 09.08.2011    source источник
comment
Да, у регулярных выражений Ruby есть много очень раздражающих проблем с Unicode. См. слайды 15–20, посвященные Ruby, из моего недавнего выступления на OSCON поддержки Unicode, особенно последний. С другой стороны, он выполняет полное сворачивание регистров — единственный движок, кроме Perl, который это делает. Но поскольку Ruby не соответствует требованиям соответствия уровня 1 UTS#18 для самых простых возможных регулярных выражений Unicode функциональность, вам в значительной степени не повезло. Боюсь, вам придется использовать ICU или Perl для реальной работы с Unicode.   -  person tchrist    schedule 09.08.2011


Ответы (3)


Отмечено Брайаном Кэндлером на ruby-talk:

  • \w соответствует только буквам и цифрам ASCII, а [[:alpha:]] соответствует полному набору букв Unicode.
  • \d соответствует только цифрам ASCII, а [[:digit:]] соответствует полному набору чисел Unicode.

Таким образом, поведение является «согласованным», и у нас есть простой обходной путь для номеров Unicode. Читая \w в тот же документ Oniguruma, мы видим текст:

\w  word character  
    Not Unicode: alphanumeric, "_" and multibyte char.  
    Unicode: General_Category -- (Letter|Mark|Number|Connector_Punctuation)

В свете реального поведения Ruby и приведенного выше текста «Not Unicode» может показаться, что документация описывает два режима: режим Unicode и режим Not Unicode, и что Ruby работает в режиме Not Unicode.

Это могло бы объяснить, почему \d не соответствует полному набору Unicode: хотя документация Oniguruma не описывает точно, что сопоставляется в режиме Not Unicode, теперь мы знаем, что поведение, задокументированное как «Unicode», не ожидается.

p "abç".scan(/\w/), "abç".scan(/[[:alpha:]]/)
#=> ["a", "b"]
#=> ["a", "b", "\u00E7"]

Читателю остается в качестве упражнения выяснить, как (если вообще) включить режим Unicode в регулярных выражениях Ruby, поскольку флаг /u (например, /\w/u) этого не делает. (Возможно, Ruby нужно перекомпилировать со специальным флагом для Oniguruma.)

Обновление: похоже, что документ Oniguruma, на который я ссылаюсь, не соответствует Ruby 1.9. См. обсуждение этого билета, включая следующие сообщения:

[Юи НАРУСЕ] "RE.txt предназначен для оригинального Oniguruma, а не для регулярного выражения Ruby 1.9. Нам может понадобиться наш собственный документ."
[Matz] "Наш Oniguruma является разветвленным. Оригинальный Oniguruma найден в geocities.jp не изменено».

Подробнее. Вот официальная документация по синтаксису регулярных выражений Ruby 1.9:
https://github.com/ruby/ruby/blob/trunk/doc/re.rdoc

person Phrogz    schedule 09.08.2011
comment
\w не соответствует латинским буквам или цифрам. Он соответствует ASCII. Латинский != ASCII. Позор позор. И я смею вас попытаться эмулировать версию UTS # 18 \w, используя ничтожные свойства Ruby: это невозможно сделать. Кроме того, все свойства POSIX не соответствуют спецификации. Некоторые даже лгут. Например, альфа не должна означать буквы!! Поэтому это отстой. - person tchrist; 10.08.2011
comment
@tchrist Спасибо за правильную терминологию; Я отредактирую свой ответ, чтобы он соответствовал. Я скромно полагаю, что «отстойная» обработка Unicode в Ruby не является подходящей темой для обсуждения переполнения стека. Кроме того, предполагая, что вы отклонили мой ответ из-за неправильной формулировки, я прошу вас прочитать вопрос еще раз и основывать свое голосование на том, правильно ли то, что я написал, отвечает на вопрос. - person Phrogz; 10.08.2011
comment
Дело в том, что он не соответствует стандарту. Он использует вещи, которые, согласно стандарту, делают одно, но используют их по-другому. Это очень обманчиво. Например, Ⓜ — это (other_)буквенный символ и символ слова, но Ruby не может сопоставить его таким образом, потому что это символ. Это ошибка. См. УТС № 18. Есть и более серьезные проблемы. - person tchrist; 10.08.2011
comment
@tchrist Я не утверждаю, что вы ошибаетесь, просто то, что вы обсуждаете, совершенно не связано с этим вопросом. Похоже, что вы используете этот вопрос, чтобы выразить свое недовольство тем, что вы считаете плохой обработкой Unicode в Ruby (ограниченным или нет регулярными выражениями). И наконец: "Ⓜ".scan(/[[:alpha:]]/) работает так, как вы говорите. - person Phrogz; 10.08.2011
comment
Вы правы: выборочные проверки показывают, что работает больше вещей, чем в прошлый раз. Не уверен, что произошло в предыдущих запусках. Странный. - person tchrist; 10.08.2011

Вместо этого попробуйте класс символов Unicode \p{N}. Это соответствует всем цифрам Unicode. Не знаю, почему \d не работает.

person Tim Pietzcker    schedule 09.08.2011
comment
Ваш \pN (на самом деле, \p{GC=Number}) соответствует больше, чем всем цифрам: он соответствует всем числам, включая нецифры. Просто цифры \p{Nd} (на самом деле, \p{GC=Decimal_Number} или \p{Numeric_Type=Decimal} в полном Юникоде). Все числа в Unicode имеют числовые значения, но не все являются цифрами. Цифры - это бигендиальные числа с основанием 10, из которых вы можете построить большие числа. ЦИФРА СЕДЬМАЯ, СЕМЬЯ НАДПИСИ И РИМСКАЯ ЦИФРА СЕМЬ — все \pN и \p{NV=7}. Однако их общие категории: Decimal_Number, Other_Number, Letter_Number. Увы, Ruby поддерживает не более только 3 свойства Unicode! - person tchrist; 09.08.2011
comment
Исправление: Ruby не поддерживает три свойства Unicode; он поддерживает только два свойства Unicode, свойство General_Category и свойство Script. Даже они не работают со всеми строками из-за ошибки проектирования Ruby strings-keelp-their-encoding. У него есть несколько поддельных свойств, которым нельзя доверять, потому что они имеют те же имена, что и в стандарте Unicode, но ведут себя иначе. Одним из них является свойство alphabetic, которое не работает в Ruby. Грустно грустно грустно. - person tchrist; 09.08.2011

\d по умолчанию будет соответствовать только числам ASCII. Вы можете вручную включить сопоставление Unicode в регулярном выражении, используя (противоречащий интуиции) синтаксис (?u):

"????".match(/(?u)\d/) # => #<MatchData "????">

В качестве альтернативы вы можете использовать стиль «posix» или «свойство unicode» в своем регулярном выражении, что не требует ручного включения сопоставления Unicode:

/[[:digit:]]/ # posix style
/\p{Nd}/ # unicode property/category style

Более подробную информацию о расширенном сопоставлении символов Unicode в Ruby можно найти в этом сообщении блога: https://idiosyncratic-ruby.com/30-regex-with-class.html

person J-_-L    schedule 26.03.2016