Рубиновый ядовитый эмодзи?

У меня установлен Ruby 3.3.4 на MacOS 14.6.1.

Предположим, у меня есть эта строка в оболочке:

$ st = "0😀2☺️4🤪6🥳8🥸"
$ echo "$st"
0😀2☺️4🤪6🥳8🥸

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

$ echo "$st" | ruby -lne 'p $_.split("")'
["0", "😀", "2", "☺", "️", "4", "🤪", "6", "🥳", "8", "🥸"]
                  ^    ^   # should be ONE grapheme

То же самое, если я прочитаю эту строку из файла:

$ cat wee_file
0😀2☺️4🤪6🥳8🥸

$ ruby -lne 'p $_.split("")' wee_file 
["0", "😀", "2", "☺", "️", "4", "🤪", "6", "🥳", "8", "🥸"]

То же самое и в IRB:

irb(main):001> File.open('/tmp/wee_file').gets.split("")
=> ["0", "😀", "2", "☺", "️", "4", "🤪", "6", "🥳", "8", "🥸", "\n"]

Но если я заменю ☺️ другим эмодзи (который тоже многобайтовый), проблема исчезнет:

$ st2 = "0😀2🐱4🤪6🥳8🥸"
$ echo "$st2" | ruby -lne 'p $_.split("")'
["0", "😀", "2", "🐱", "4", "🤪", "6", "🥳", "8", "🥸"]

# also from a file and also in IRB..

Есть идеи, почему смайлик ☺️ дает такой результат?

Остальные являются настоящими одиночными символами эмодзи, например. 😀 — это U+1F600 (ухмыляющееся лицо) и т. д. Но ☺️ вставляется в U+FE0F (селектор варианта — 16) как вариант U+263A, гораздо более старого улыбающегося лица (как показано на вашей распечатке).

matt 20.08.2024 17:18
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
2
1
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это потому, что ☺️ состоит из двух символов:

  1. U+263A (Белое улыбающееся лицо)
  2. ◌️ U+FE0F (Выбор варианта-16)

Последний используется для запроса представления смайликов для предыдущего персонажа.

"☺️".codepoints.map { |c| c.to_s(16) }
#=> ["263a", "fe0f"]

Вы можете получить ожидаемый результат с помощью grapheme_clusters или перечислить их с помощью each_grapheme_cluster :

"0😀2☺️4🤪6🥳8🥸".grapheme_clusters
#=> ["0", "😀", "2", "☺️", "4", "🤪", "6", "🥳", "8", "🥸"]

И "0😀2☺️4🤪6🥳8🥸".scan(/\X/) тоже работает...

dawg 20.08.2024 17:12

@dawg ох, действительно, я не знал, что \X поддерживается Ruby. Кажется, в документах этого нет.

Stefan 20.08.2024 17:19

Внутри each_grapheme_cluster на самом деле, похоже, используется механизм регулярных выражений \X для перечисления графем, см. string.c#L9455-L9457 и string.c#L9379

Stefan 20.08.2024 17:28

Существует также String#grapheme_clusters, который возвращает точно такой же результат, как each_grapheme_cluster.to_a, но с меньшим количеством промежуточных объектов.

Holger Just 20.08.2024 18:02

@HolgerJust, должно быть, это пропустил. Я обновил ответ соответственно.

Stefan 20.08.2024 20:00

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