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.

16
demandé sur Sergio Tulentsev 2011-01-13 17:22:25

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 .

39
répondu steenslag 2014-12-31 20:19:50

é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.

9
répondu Josh Glover 2011-01-13 16:31:09

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.

1
répondu jpatokal 2012-06-04 01:26:29

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
0
répondu Henrik N 2017-05-23 12:09:56