В юникодом некоторые символы, такие как ä
, могут быть представлены двумя способами. Они могут быть одиночными кодовыми точками, такими как U + 00E4, в случае ä
, или они могут быть сформированы из «базового» символа, за которым следует combining character, например a
, за которым следует U + 0308 (КОМБИНИРОВАННАЯ ОПЕРАЦИЯ). В последнем случае комбинированный символ состоит из двух кодовых точек, а метод Ruby String#length
возвращает только общее количество кодовых точек, чтобы вы могли получать разные значения для длин кажущихся одних и тех же строк.
s1 = "ä" # single codepoint
s2 = "a" # 'base' letter
s3 = "a\u0308" # base letter + combining character
[s1, s2, s3].each do |s|
puts "Letter: #{s}"
puts "Bytes: #{s.bytes}"
puts "Codepoints: #{s.codepoints}"
puts "Length: #{s.length}"
puts
end
Выход:
Letter: ä
Bytes: [195, 164]
Codepoints: [228]
Length: 1
Letter: a
Bytes: [97]
Codepoints: [97]
Length: 1
Letter: ä
Bytes: [97, 204, 136]
Codepoints: [97, 776]
Length: 2
(bytes
это кодировка UTF-8 символов в UTF-8 некоторые символы кодируются в виде нескольких байт - это отдельный вопрос от улавливающих символов. .)
Ruby сам по себе (в настоящее время) не имеет большой поддержки для решения таких проблем в Юникоде, поэтому вам нужно использовать внешнюю библиотеку, такую как UnicodeUtils. Идея length
может стать довольно неясной при разговоре о разных языках (что считается «единственным символом». Вы можете использовать метод display_width
, который, вероятно, даст то, что вы хотите для латинских скриптов. Другая возможность - использовать normalized form, который убеждается все символы представлены точно так же, как все разложено на conbining символы, или все (что у них есть), используя один символ:
require 'unicode_utils'
combined = "a\u0308"
single = "ä"
# nfc - normalized form composed - use a single code point if possible
puts UnicodeUtils.nfc(combined).length # => 1
puts UnicodeUtils.nfc(single).length # => 1
# nfd - normalized form decomposed - always use combining characters
puts UnicodeUtils.nfd(combined).length # => 2
puts UnicodeUtils.nfd(single).length # => 2
Теперь это сделал трюк – Besi
@Besi Он должен, ждать, пока * 2.2 * as * matz * said. :-) –
@ArupRakshit +1 за информацию, я думаю, что вы должны работать на выборах SO :) Вы много делаете за тег ruby на SO, спасибо за ваши усилия – bjhaid