Comment puis-je détecter certains caractères Unicode dans une chaîne de caractères dans Ruby?
avec une chaîne de caractères Ruby 1.8.7 (sans le formidable moteur d'expression régulière Oniguruma qui supporte les propriétés Unicode avec p {}), j'aimerais pouvoir déterminer si la chaîne de caractères contient un ou plusieurs caractères chinois, japonais ou coréens; c.-à-d.
class String
def contains_cjk?
...
end
end
>> '日本語'.contains_cjk?
=> true
>> '광고 프로그램'.contains_cjk?
=> true
>> '艾弗森将退出篮坛'.contains_cjk?
=> true
>> 'Watashi ha bakana gaijin desu.'.contains_cjk?
=> false
je soupçonne que cela va se résumer à voir si l'un des caractères dans la chaîne sont dans le Unihan CJKV Unicode blocks, mais j'ai pensé qu'il était bon de se demander si quelqu'un connaît une solution existante à Ruby.
4 réponses
(ruby 1.9.2)
#encoding: UTF-8
class String
def contains_cjk?
!!(self =~ /\p{Han}|\p{Katakana}|\p{Hiragana}|\p{Hangul}/)
end
end
strings= ['日本', '광고 프로그램', '艾弗森将退出篮坛', 'Watashi ha bakana gaijin desu.']
strings.each{|s| puts s.contains_cjk?}
#true
#true
#true
#false
\p{} correspond au script Unicode d'un personnage.
Les scripts suivants sont pris en charge: arabe, arménien, Balinais, Bengali, Bopomofo, Braille, Buginese, Buhid, Canadian_Aboriginal, Carian, Cham, Cherokee, Common, Coptic, Cuneiform, Cyrillique, Deseret, Devanagari, Ethiopic, Georgian, Glagolitic, Gothic, Greek, Gujarati, Gurmukhi, Han, Hangul, Hanunoo, Hebrew, Hiragana, Inherited, Kannada, Katakana, kayah_li, Kharoshthi, Khmer, Lao, Latin, Lepcha, Limbu, Linear_B, Lycian, Lydian, Malayalam, mongolien, Myanmar, New_Tai_Lue, Nko, Ogham, Ol_Chiki, Old_Italic, Old_Persian, Oriya, Osmanya, Phags_Pa, phénicien, Rejang, Runic, Saurashtra, Shavian, Sinhala, Sundanese, Syloti_Nagri, Syriac, Tagalog, Tagbanwa, Tai_Le, Tamil, Telugu, Thaana, Thai, Tibétain, Tifinagh, Ugaritic, Vai, et Yi.
Wow. Ruby Regexp source .
étant donné ma contrainte Ruby 1.8.7, c'est le mieux que je puisse faire:
class String
CJKV_RANGES = [
(0xe2ba80..0xe2bbbf),
(0xe2bfb0..0xe2bfbf),
(0xe38080..0xe380bf),
(0xe38180..0xe383bf),
(0xe38480..0xe386bf),
(0xe38780..0xe387bf),
(0xe38880..0xe38bbf),
(0xe38c80..0xe38fbf),
(0xe39080..0xe4b6bf),
(0xe4b780..0xe4b7bf),
(0xe4b880..0xe9bfbf),
(0xea8080..0xea98bf),
(0xeaa080..0xeaaebf),
(0xeaaf80..0xefbfbf),
]
def contains_cjkv?
each_char do |ch|
return true if CJKV_RANGES.any? {|range| range.member? ch.unpack('H*').first.hex }
end
false
end
end
strings = ['日本', '광고 프로그램', '艾弗森将退出篮坛', 'Watashi ha bakana gaijin desu.']
strings.each {|s| puts s.contains_cjkv? }
#true
#true
#true
#false
Assez hacktacular, mais il fonctionne. Il détecte effectivement une variété de scripts Indic ainsi,il devrait donc probablement être vraiment appelé contains_asian?
peut-être que je devrais gemmer ça pour d'autres pauvres hackers i18n coincés avec Ruby 1.8.
j'ai écrit un petit bijou qui emballe l'approche dans la réponse de steenslag ci-dessus:
https://github.com/jpatokal/script_detector
il peut aussi tenter de faire la différence entre le Japonais, Le coréen, le chinois simplifié et le Chinois traditionnel, bien qu'en raison de la complexité de L'unification des Han il ne fonctionne de façon fiable qu'avec de grandes plaques de texte.
solution Ruby 1.8 basée sur ce code et en utilisant l'API de Josh Glover de la solution sur ce thread:
class String
CJKV_RANGES = [
(0x4E00..0x9FFF),
(0x3400..0x4DBF),
(0x20000..0x2A6DF),
(0x2A700..0x2B73F),
]
def contains_cjkv?
unpack("U*").any? { |char|
CJKV_RANGES.any? { |range| range.member?(char) }
}
end
end