2011-12-19 12 views
8

के बावजूद सीरियलाइज्ड हैश के साथ एन्कोडिंग की समस्याएं मैंने अभी रुबी 1.9.2 से रूबी 1.9.3p0 (2011-10-30 संशोधन 33570) से अपडेट किया है। मेरे रेल एप्लिकेशन postgresql का उपयोग डेटाबेस बैकएंड के रूप में करता है। सिस्टम लोकेल यूटीएफ 8 है, जैसा डाटाबेस एन्कोडिंग है। रेल आवेदन के डिफ़ॉल्ट एन्कोडिंग भी यूटीएफ 8 है। मेरे पास चीनी उपयोगकर्ता हैं जो चीनी वर्णों के साथ-साथ अंग्रेजी वर्णों को इनपुट करते हैं। तारों को यूटीएफ 8 एन्कोडेड तारों के रूप में संग्रहीत किया जाता है।रेल: यूटीएफ 8

रेल संस्करण: 3.0.9

अद्यतन डेटाबेस में मौजूदा चीनी तार के कुछ नहीं रह गया है सही ढंग से प्रदर्शित कर रहे हैं के बाद से। यह सभी तारों को प्रभावित नहीं करता है, लेकिन केवल वे जो धारावाहिक हैश का हिस्सा हैं। सादे तारों के रूप में संग्रहीत सभी अन्य तार अभी भी सही प्रतीत होते हैं।


उदाहरण:

यह एक धारावाहिक हैश कि डेटाबेस में एक UTF8 स्ट्रिंग के रूप में संग्रहीत किया जाता है:

broken = "--- !map:ActiveSupport::HashWithIndifferentAccess \ncheckbox: \"1\"\nchoice: \"Round Paper Clips \\xEF\\xBC\\x88\\xE5\\x9B\\x9E\\xE5\\xBD\\xA2\\xE9\\x92\\x88\\xEF\\xBC\\x89\\r\\n\"\ninfo: \"10\\xE7\\x9B\\x92\"\n" 

आदेश में एक गहरे लाल रंग का हैश को यह स्ट्रिंग परिवर्तित करने के लिए, मैं इसे YAML.load के साथ deserialize:

broken_hash = YAML.load(broken) 

यह विकृत सामग्री के साथ एक हैश रिटर्न:

{"checkbox"=>"1", "choice"=>"Round Paper Clips ï¼\u0088å\u009B\u009Eå½¢é\u0092\u0088ï¼\u0089\r\n", "info"=>"10ç\u009B\u0092"} 

विकृत सामान जा UTF8 एन्कोड करने के लिए चीनी माना जाता है। broken_hash['info'].encoding मुझे बताता है कि रूबी सोचता है कि यह #<Encoding:UTF-8> है। मैं असहमत हूं।

दिलचस्प बात यह है कि, अन्य सभी तार जो ठीक दिखने से पहले क्रमबद्ध नहीं थे, हालांकि। उसी रिकॉर्ड में एक अलग फ़ील्ड में चीनी वर्ण होते हैं जो ठीक दिखते हैं --- रेल कंसोल, psql कंसोल और ब्राउज़र में। प्रत्येक स्ट्रिंग --- कोई फर्क नहीं पड़ता कि धारावाहिक हैश या सादा स्ट्रिंग --- डेटाबेस में सहेजा गया है क्योंकि अद्यतन ठीक दिखता है।


मैं रूबी के दावे कि यह पहले से ही था UTF-8 और निश्चित रूप से मैं में विफल रहा है के बावजूद UTF-8 में एक संभव गलत एन्कोडिंग (जीबी 2312 या एएनएसआई) की तरह से विकृत पाठ परिवर्तित करने के लिए कोशिश की।

require 'iconv' 
Iconv.conv('UTF-8', 'GB2312', broken_hash['info']) 

यह विफल रहता है क्योंकि गहरे लाल रंग का क्या स्ट्रिंग में अवैध दृश्यों से कोई लेना देना नहीं जानता है: यह कोड मैं प्रयोग किया जाता है।

मैं वास्तव में बस सभी पुरानी, ​​संभावित रूप से टूटी धारावाहिक हैश तारों को ठीक करने के लिए एक स्क्रिप्ट चलाने के लिए चाहता हूं और इसके साथ किया जाना चाहिए। क्या इन टूटे तारों को फिर से चीनी जैसा दिखने का कोई तरीका है?


मैं सिर्फ कच्चे स्ट्रिंग में एन्कोडेड UTF-8 स्ट्रिंग के साथ खेला जाता है (जिसे "टूटा" ऊपर के उदाहरण में)।

chinese = "\\xEF\\xBC\\x88\\xE5\\x9B\\x9E\\xE5\\xBD\\xA2\\xE9\\x92\\x88\\xEF\\xBC\\x89\\r\\n\"

मैंने देखा है कि यह न छोड़ने (भागने बैकस्लैश को हटाने) द्वारा एक वास्तविक UTF-8 एन्कोडेड स्ट्रिंग में बदलने के लिए आसान है: यह चीनी स्ट्रिंग है जो धारावाहिक स्ट्रिंग में एन्कोड किया गया है है।"(回形针)\r\n"

बात अलग केवल जब मैं YAML.load(...) का प्रयोग कर एक गहरे लाल रंग का हैश को स्ट्रिंग परिवर्तित करने के लिए आते हैं:

chinese_ok = "\xEF\xBC\x88\xE5\x9B\x9E\xE5\xBD\xA2\xE9\x92\x88\xEF\xBC\x89\r\n"

यह एक उचित UTF-8 एन्कोड चीनी स्ट्रिंग देता है। शायद इसे YAML.load पर खिलाया जाने से पहले कच्ची स्ट्रिंग को संसाधित करना चाहिए। बस मुझे आश्चर्य है कि ऐसा क्यों है ...


दिलचस्प! यह संभवतः वाईएएमएल इंजन "मनोविज्ञान" के कारण होता है जिसका उपयोग डिफ़ॉल्ट रूप से 1.9.3 में किया जाता है। मैंने YAML::ENGINE.yamler = 'syck' के साथ "सिक" इंजन पर स्विच किया और टूटे तारों को सही ढंग से पार्स किया गया।

+0

धारावाहिक हैश के लिए कॉलम प्रकार क्या है? –

+0

@muistooshort: कॉलम प्रकार 'text' है। – rekado

+0

क्या होता है यदि आप कॉलम को 'बाइनरी' में बदलते हैं? इसे स्ट्रिंग को "8 बिट ASCII" (यानी कच्चे बाइट्स) के रूप में प्राप्त करना चाहिए और शायद वह 'YAML.load' को आकार में लाएगा। एक त्वरित परीक्षण के रूप में आप 'YAML.load (टूटा हुआ) 'से पहले' broken.force_encoding ('बाइनरी')' 'कर सकते हैं। –

उत्तर

12

ऐसा लगता है कि दो उपलब्ध वाईएएमएल इंजन "सिक" और "साइको" के व्यवहार में अंतर आया है। syck को YAML इंजन सेट करने के लिए:

YAML::ENGINE.yamler = 'psych'

"syck" इंजन तार के रूप में उम्मीद संसाधित करता है और करने के लिए उन्हें धर्मान्तरित:

YAML::ENGINE.yamler = 'syck'

साइक को वापस YAML इंजन सेट करने के लिए उचित चीनी तारों के साथ हैश। जब "मनोविज्ञान" इंजन का उपयोग किया जाता है (रूबी 1.9.3 में डिफ़ॉल्ट), रूपांतरण परिणाम गड़बड़ी तारों में होते हैं।

उपर्युक्त रेखा (दोनों में से पहला) को config/application.rb में जोड़ना इस समस्या को हल करता है। "सिक" इंजन अब बनाए रखा नहीं जाता है, इसलिए मुझे शायद "मनोविज्ञान" के लिए तारों को स्वीकार्य बनाने के लिए मुझे कुछ समय खरीदने के लिए इस कामकाज का उपयोग करना चाहिए।

+0

लगता है कि हम एक ही समय में एक ही चीज़ को देख रहे थे। मैं सब कुछ मनोविज्ञान प्रारूप में फिर से एन्कोड करता हूं या वाईएएमएल को पूरी तरह से हटा देता हूं और जेएसओएन या कुछ अन्य स्थिर/पोर्टेबल प्रारूप का उपयोग करके मैन्युअल रूप से क्रमबद्ध करता हूं। –

+0

बीटीडब्ल्यू, आप अपना खुद का जवाब स्वीकार कर सकते हैं और मुझे लगता है कि इस मामले में ऐसा करने का अर्थ है। –

9

1.9.3 NEWS file से:

* yaml 
    * The default YAML engine is now Psych. You may downgrade to syck by setting 
    YAML::ENGINE.yamler = 'syck'. 

जाहिर Syck और साइक YAML इंजन अलग और असंगत तरीके से गैर- ASCII तार का इलाज।

एक हैश को देखते हुए की तरह तुम हो:

h = { 
    "checkbox" => "1", 
    "choice" => "Round Paper Clips (回形针)\r\n", 
    "info"  => "10盒" 
} 

वर्ष Syck इंजन का उपयोग करना:

>> YAML::ENGINE.yamler = 'syck' 
>> h.to_yaml 
=> "--- \ncheckbox: "1"\nchoice: "Round Paper Clips \\xEF\\xBC\\x88\\xE5\\x9B\\x9E\\xE5\\xBD\\xA2\\xE9\\x92\\x88\\xEF\\xBC\\x89\\r\\n"\ninfo: "10\\xE7\\x9B\\x92"\n" 

हम आपको वर्तमान में अपने डेटाबेस में बदसूरत डबल बैकस्लैश प्रारूप मिलता है। साइको पर स्विचिंग:

>> YAML::ENGINE.yamler = 'psych' 
=> "psych" 
>> h.to_yaml 
=> "---\ncheckbox: '1'\nchoice: ! "Round Paper Clips (回形针)\\r\\n"\ninfo: 10盒\n" 

तार सामान्य यूटीएफ -8 प्रारूप में रहते हैं। हम स्वयं एन्कोडिंग अप पेंच तो लैटिन -1 होने के लिए:

>> Iconv.conv('UTF-8', 'ISO-8859-1', "\xEF\xBC\x88\xE5\x9B\x9E\xE5\xBD\xA2\xE9\x92\x88\xEF\xBC\x89") 
=> "ï¼\u0088å\u009B\u009Eå½¢é\u0092\u0088ï¼\u0089" 

तो हम बकवास की तरह है कि आप देख रहे हैं मिलता है।

वाईएएमएल दस्तावेज अपेक्षाकृत पतला है इसलिए मुझे नहीं पता कि क्या आप पुराने सिक प्रारूप को समझने के लिए मन को मजबूर कर सकते हैं। मुझे लगता है कि आप तीन विकल्प हैं:

  1. , पुराने असमर्थित और पदावनत Syck इंजन का उपयोग आप YAML::ENGINE.yamler = 'syck' लिए इससे पहले कि आप कुछ भी YAML आवश्यकता होगी।
  2. साइक का उपयोग करके अपने सभी वाईएएमएल लोड और डीकोड करें और फिर साइको का उपयोग करके इसे फिर से एन्कोड करें और सहेजें।
  3. JSON (या कुछ अन्य स्थिर, अनुमानित, और पोर्टेबल टेक्स्ट प्रारूप) का उपयोग करके मैन्युअल रूप से क्रमबद्ध/deserializing के पक्ष में serialize का उपयोग करना बंद करें या एक एसोसिएशन तालिका का उपयोग करें ताकि आप क्रमबद्ध डेटा को संग्रहीत नहीं कर रहे हों।
+0

हा, यह अच्छा है: मैंने इसे समझने के एक मिनट बाद अपना उत्तर सबमिट कर दिया है। अब मैंने अस्थायी रूप से उपयोग किए जाने वाले "सिक" को मजबूर कर अनुप्रयोगों को ठीक कर दिया है। आखिरकार, मुझे इसे कठिन तरीके से करना होगा और "मनोविज्ञान" के साथ सबकुछ फिर से एन्कोड करना होगा। वास्तव में असंगत परिवर्तन पसंद नहीं है। – rekado

+2

@rekado: मैं पूरी तरह से वाईएएमएल से दूर चलेगा, मुझे लगता है कि यह डेटा क्रमिकरण के लिए एक भयानक प्रारूप है और रेल लोग इसे 'धारावाहिक' के लिए उपयोग करने के लिए मूर्ख थे। लेकिन मैं भी एक प्राकृतिक जन्मजात :) –

संबंधित मुद्दे