2012-09-21 17 views
10

This RabbitMQ page कहता है:रूबी: बाइट-लंबाई से एक UTF-8 स्ट्रिंग सीमित

कतार नामों UTF-8 वर्णों की अधिकतम 255 बाइट्स हो सकता है।

रूबी (1.9.3) में, मैं एक चरित्र के बीच में तोड़ने के बिना बाइट-गिनती द्वारा यूटीएफ -8 स्ट्रिंग को कैसे छोटा कर दूंगा? परिणामी स्ट्रिंग बाइट सीमा में फिट होने वाली सबसे लंबी संभव यूटीएफ -8 स्ट्रिंग होनी चाहिए।

उत्तर

1

मुझे लगता है कि मैं कुछ है कि काम करता है पाया।

def limit_bytesize(str, size) 
    str.encoding.name == 'UTF-8' or raise ArgumentError, "str must have UTF-8 encoding" 

    # Change to canonical unicode form (compose any decomposed characters). 
    # Works only if you're using active_support 
    str = str.mb_chars.compose.to_s if str.respond_to?(:mb_chars) 

    # Start with a string of the correct byte size, but 
    # with a possibly incomplete char at the end. 
    new_str = str.byteslice(0, size) 

    # We need to force_encoding from utf-8 to utf-8 so ruby will re-validate 
    # (idea from halfelf). 
    until new_str[-1].force_encoding('utf-8').valid_encoding? 
    # remove the invalid char 
    new_str = new_str.slice(0..-2) 
    end 
    new_str 
end 

उपयोग:

>> limit_bytesize("abc\u2014d", 4) 
=> "abc" 
>> limit_bytesize("abc\u2014d", 5) 
=> "abc" 
>> limit_bytesize("abc\u2014d", 6) 
=> "abc—" 
>> limit_bytesize("abc\u2014d", 7) 
=> "abc—d" 

अद्यतन ...

active_support बिना

विघटित व्यवहार:

>> limit_bytesize("abc\u0065\u0301d", 4) 
=> "abce" 
>> limit_bytesize("abc\u0065\u0301d", 5) 
=> "abce" 
>> limit_bytesize("abc\u0065\u0301d", 6) 
=> "abcé" 
>> limit_bytesize("abc\u0065\u0301d", 7) 
=> "abcéd" 

active_support साथ विघटित व्यवहार:

>> limit_bytesize("abc\u0065\u0301d", 4) 
=> "abc" 
>> limit_bytesize("abc\u0065\u0301d", 5) 
=> "abcé" 
>> limit_bytesize("abc\u0065\u0301d", 6) 
=> "abcéd" 
1

कैसे इस बारे में:

s = "δogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδogδog" 
count = 0 
while true 
    more_truncate = "a" + (255-count).to_s 
    s2 = s.unpack(more_truncate)[0] 
    s2.force_encoding 'utf-8' 

    if s2[-1].valid_encoding? 
    break 
    else 
    count += 1 
    end 
end 

s2.force_encoding 'utf-8' 
puts s2 
+0

यह काम करता है, लेकिन क्या हुआ अगर स्ट्रिंग बहुत बड़ा है? एक समय में एक utf-8 char को हटाने के लिए यह बहुत अक्षम हो सकता है। – Kelvin

+0

@ केल्विन उत्तर संपादित किया गया है। यह अब बहुत बेहतर होना चाहिए।चूंकि utf-8 char 6 बाइट से अधिक नहीं होगा, लूप जल्द ही समाप्त हो जाएगा। – halfelf

+0

अपूर्ण लगता है - 's' नहीं बदला है। क्या आपको नई स्ट्रिंग प्राप्त करने के लिए 's2' पैक करना होगा? याद रखें कि आउटपुट भी utf-8 होना चाहिए। – Kelvin

9

bytesize आपको बाइट्स में स्ट्रिंग की लंबाई देगा (जब तक स्ट्रिंग का एन्कोडिंग ठीक से सेट हो) स्लाइस जैसे ऑपरेशन स्ट्रिंग को उलझाना नहीं होगा।

एक साधारण प्रक्रिया यदि आप चालाक आप पहली बार 63 वर्ण कॉपी चाहते हैं सीधे बाद से किसी भी यूनिकोड चरित्र utf-8 में सबसे 4 बाइट पर है जा रहे थे सिर्फ स्ट्रिंग

s.each_char.each_with_object('') do|char, result| 
    if result.bytesize + char.bytesize > 255 
    break result 
    else 
    result << char 
    end 
end 

के माध्यम से पुनरावृति करने के लिए किया जाएगा।

ध्यान दें कि यह अभी भी सही नहीं है। उदाहरण के लिए, कल्पना करें कि आपकी स्ट्रिंग के अंतिम 4 बाइट वर्ण 'ई' हैं और तीव्र उच्चारण का संयोजन करते हैं। पिछले 2 बाइट्स को स्लाइस करने से एक स्ट्रिंग उत्पन्न होती है जो अभी भी utf8 है लेकिन उपयोगकर्ता द्वारा जो देखा जाता है, उसके संदर्भ में 'é' से 'e' तक आउटपुट बदल जाएगा, जो पाठ का अर्थ बदल सकता है। यह शायद एक बड़ा सौदा नहीं है जब आप केवल RabbitMQ कतारों का नाम दे रहे हैं लेकिन अन्य परिस्थितियों में महत्वपूर्ण हो सकता है। उदाहरण के लिए, फ्रांसीसी में 'न्यू पॉलिसियर ट्यू' का अर्थ न्यूजलेटर शीर्षक 'मतलब है कि एक पुलिसकर्मी मारे गए' जबकि 'अन पुलिसकर्मी' का अर्थ है 'एक पुलिसकर्मी हत्या'।

+4

+1 सिर्फ पुलिसकर्मी उदाहरण के लिए :)। Google अनुवाद यह पुष्टि करता है। हालांकि उच्चारण काफी अलग ध्वनि है। – Kelvin

+0

बस इतना ही जानता है कि "संयुक्त चरित्र" मुद्दा केवल [विघटित वर्ण] (http://en.wikipedia.org/wiki/Precomposed_character) के साथ होता है। कोई समस्या नहीं है यदि ई-तीव्र, आदि एक एकल है। – Kelvin

+0

आप इसे कैनोनिकल फॉर्म सी में परिवर्तित करके इससे बच सकते हैं पहले –

7

रेल के लिए> = 3.0 आपके पास ActiveSupport :: Multibyte :: वर्ण सीमा विधि है।

एपीआई डॉक्स से:

- (Object) limit(limit) 

सीमा पात्रों को तोड़ने के बिना बाइट्स के एक नंबर करने के लिए स्ट्रिंग के बाइट आकार। प्रयोग योग्य जब किसी स्ट्रिंग के लिए भंडारण किसी कारण से सीमित है।

उदाहरण:

'こんにちは'.mb_chars.limit(7).to_s # => "こん" 
+0

अच्छा, यदि आप ActiveSupport> = 3.0 का उपयोग कर रहे हैं तो यह सबसे अच्छा समाधान जैसा लगता है। यदि आपको विघटित वर्ण हैं (मेरा उत्तर देखें) तो आपको अभी भी 'mb_chars.compose.limit' का उपयोग करने की आवश्यकता होगी)। – Kelvin