रूबी 1.9: यूटीएफ -8 में अमान्य बाइट अनुक्रम


109

मैं रूबी (1.9) में एक क्रॉलर लिख रहा हूं जो बहुत सारे यादृच्छिक साइटों से बहुत सारे HTML का उपभोग करता है।
लिंक निकालने की कोशिश करते समय, मैंने .scan(/href="(.*?)"/i)nokogiri / hpricot (मेजर स्पीडअप) के बजाय सिर्फ उपयोग करने का निर्णय लिया । समस्या यह है कि मुझे अब बहुत सारी " invalid byte sequence in UTF-8" त्रुटियां प्राप्त हुई हैं।
मुझे जो समझ में आया, उसके लिए net/httpपुस्तकालय के पास कोई विशेष विकल्प नहीं है और जो सामान आता है, वह मूल रूप से ठीक से टैग नहीं किया गया है।
उस आने वाले डेटा के साथ वास्तव में काम करने का सबसे अच्छा तरीका क्या होगा? मैंने .encodeप्रतिस्थापित और अमान्य विकल्पों के साथ प्रयास किया, लेकिन अभी तक कोई सफलता नहीं मिली ...


ऐसा कुछ जो वर्णों को तोड़ सकता है, लेकिन स्ट्रिंग को अन्य पुस्तकालयों के लिए वैध रखता है: मान्य_string = untrusted_string.unpack ('C *')। पैक ('U *')
Marc Seeger

सटीक मुद्दा होने पर, अन्य समान समाधानों की कोशिश की। प्यार नहीं। मार्क की कोशिश की, लेकिन यह सब कुछ garble लगता है। क्या आपको यकीन है 'U*'नाश कर देती है 'C*'?
जॉर्डन फेल्डस्टीन

नहीं, यह नहीं है :) मैंने सिर्फ एक वेबक्रॉलर में इस्तेमाल किया था जहां मुझे 3 पार्टी लाइब्रेरी के बारे में परवाह है कि मैं एक वाक्य के बारे में यहां और वहां से ज्यादा क्रैश नहीं करता हूं।
मार्क सीगर

जवाबों:


172

रूबी 1.9.3 में अमान्य UTF-8 दृश्यों को "अनदेखा" करने के लिए String.encode का उपयोग करना संभव है। यहाँ एक स्निपेट है जो 1.8 ( iconv ) और 1.9 ( स्ट्रिंग # एनकोड ) दोनों में काम करेगा।

require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
  file_contents.encode!('UTF-8', 'UTF-8', :invalid => :replace)
else
  ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
  file_contents = ic.iconv(file_contents)
end

या यदि आपके पास वास्तव में परेशानी भरा इनपुट है, तो आप UTF-8 से UTF-16 और वापस UTF-8 में दोहरा रूपांतरण कर सकते हैं:

require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
  file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '')
  file_contents.encode!('UTF-8', 'UTF-16')
else
  ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
  file_contents = ic.iconv(file_contents)
end

3
कुछ समस्याग्रस्त इनपुट के साथ, मैं UTF-8 से UTF-16 में एक डबल रूपांतरण का उपयोग करता हूं और फिर UTF-8 file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '') file_contents.encode!('UTF-8', 'UTF-16')
रुबेनलागुना

7
का विकल्प भी है force_encoding। यदि आपके पास एक UT8-8 के रूप में ISO8859-1 पढ़ा है (और इस तरह स्ट्रिंग में अमान्य UTF-8 शामिल हैं) तो आप इसे_8_ring_force_encoding ("ISO8859-1") के साथ ISO8859-1 के रूप में "रीइंटरप्रिट" कर सकते हैं और बस काम कर सकते हैं उस स्ट्रिंग के साथ इसकी वास्तविक एन्कोडिंग में।
RubenLaguna

3
उस डबल एनकोड ट्रिक ने मेरे बेकन को बचा लिया! मुझे आश्चर्य है कि इसकी आवश्यकता क्यों है?
जॉन्ह

1
मैं उन पंक्तियों को कहाँ रखूँ?
लेफ्सलर

5
मुझे लगता है कि डबल रूपांतरण कार्य करता है क्योंकि यह एन्कोडिंग रूपांतरण (और इसके साथ अमान्य वर्णों की जांच) को बाध्य करता है। यदि स्रोत स्ट्रिंग पहले से ही UTF-8 में एन्कोडेड है, तो बस कॉलिंग .encode('UTF-8')एक न-ऑप है, और कोई चेक नहीं चलाया जाता है। एनकोड के लिए रूबी कोर डॉक्यूमेंटेशन । हालांकि, इसे UTF-16 में परिवर्तित करने से पहले अमान्य बाइट अनुक्रमों के लिए सभी जांचों को चलाने के लिए मजबूर किया जाता है, और प्रतिस्थापन आवश्यकतानुसार किए जाते हैं।
जो हंड

79

स्वीकृत उत्तर और न ही अन्य उत्तर मेरे लिए काम करते हैं। मुझे यह पोस्ट मिली जिसने सुझाव दिया

string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')

इससे मेरे लिए समस्या ठीक हो गई।


1
इसने मेरे लिए समस्या को ठीक कर दिया और मुझे गैर-वंचित तरीकों का उपयोग करना पसंद है (मेरे पास अब रूबी 2.0 है)।
ला-कॉमरेडेजा

1
यह एक ही काम करता है! मैंने उपरोक्त सभी समाधानों की कोशिश की है, उनमें से कोई भी स्ट्रिंग काम नहीं करता है जो कि परीक्षण में उपयोग किया जाता है "fdsfdsf dfsf sfds fs sdf <div> hello <p> fooo ??? {! @ # $% ^ & * () _ +} <! / p> </ div> \ xEF \ xBF \ xBD \ xef \ xbc \ x9c <div> \ xc2 \ x90 </ div> \ xc2 \ x90 "
चिहुंग यू

1
दूसरा तर्क 'बाइनरी' किसके लिए है?
हेनली चिउ

24

मेरा वर्तमान समाधान चलाना है:

my_string.unpack("C*").pack("U*")

यह कम से कम उन अपवादों से छुटकारा दिलाएगा जो मेरी मुख्य समस्या थी


3
मैं इस पद्धति का उपयोग कर रहा हूं valid_encoding?जिसके संयोजन में यह पता लगता है कि कुछ गलत है। val.unpack('C*').pack('U*') if !val.valid_encoding?
आरोन जिब्राल्टर

यह एक मेरे लिए काम किया। सफलतापूर्वक मेरी \xB0पीठ को डिग्री प्रतीकों में परिवर्तित करता है । यहां तक ​​कि valid_encoding?सच वापस आता है, लेकिन मैं अभी भी जांच करता हूं कि क्या यह ऊपर और आमिर के उत्तर का उपयोग करके अपमानजनक चरित्रों को बाहर नहीं निकालता है string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: ''):। मैंने force_encodingमार्ग की कोशिश भी की थी लेकिन वह असफल रहा।
हम्सटर

यह भी खूब रही। धन्यवाद।
d_ethier

8

इसे इस्तेमाल करे:

def to_utf8(str)
  str = str.force_encoding('UTF-8')
  return str if str.valid_encoding?
  str.encode("UTF-8", 'binary', invalid: :replace, undef: :replace, replace: '')
end

मेरे मामले के लिए सबसे अच्छा जवाब! धन्यवाद
एल्डो

4

मैं आपको HTML पार्सर का उपयोग करने की सलाह देता हूं। जरा सबसे तेज खोजो।

HTML पार्स करना उतना आसान नहीं है जितना यह लग सकता है।

ब्राउज़रों ने UTF-8 HTML दस्तावेज़ों में अमान्य UTF-8 अनुक्रम पार्स कर दिए, बस " " प्रतीक डाल दिया। इसलिए एक बार HTML में अमान्य UTF-8 अनुक्रम के परिणामस्वरूप परिणामी पाठ पार्स हो जाता है।

यहां तक ​​कि विशेषता मानों के अंदर भी आपको HTML संस्थाओं को amp की तरह डिकोड करना होगा

यहाँ एक बड़ा सवाल है कि आप एक नियमित अभिव्यक्ति के साथ HTML को मज़बूती से पार्स क्यों नहीं कर सकते हैं: RegEx, XHTML स्व-निहित टैग को छोड़कर खुले टैग से मेल खाता है


2
मैं regexp रखना पसंद करूंगा क्योंकि यह लगभग 10 गुना तेज है और मैं वास्तव में html को सही ढंग से पार्स नहीं करना चाहता हूं, लेकिन केवल लिंक निकालना चाहता हूं। मुझे केवल करने से माणिक में अमान्य भागों को बदलने में सक्षम होना चाहिए: ok_string = bad_string.encode ("UTF-8", {: अमान्य =>: प्रतिस्थापित करें: undef =>: प्रतिस्थापित}) लेकिन यह प्रतीत होता है काम :(
मार्क सीगर

3

यह काम करने लगता है:

def sanitize_utf8(string)
  return nil if string.nil?
  return string if string.valid_encoding?
  string.chars.select { |c| c.valid_encoding? }.join
end

3
attachment = file.read

begin
   # Try it as UTF-8 directly
   cleaned = attachment.dup.force_encoding('UTF-8')
   unless cleaned.valid_encoding?
     # Some of it might be old Windows code page
     cleaned = attachment.encode( 'UTF-8', 'Windows-1252' )
   end
   attachment = cleaned
 rescue EncodingError
   # Force it to UTF-8, throwing out invalid bits
   attachment = attachment.force_encoding("ISO-8859-1").encode("utf-8", replace: nil)
 end

2

मैंने स्ट्रिंग का सामना किया है, जिसमें अंग्रेजी, रूसी और कुछ अन्य अक्षर का मिश्रण था, जो अपवाद का कारण बना। मुझे केवल रूसी और अंग्रेजी की आवश्यकता है, और यह वर्तमान में मेरे लिए काम करता है:

ec1 = Encoding::Converter.new "UTF-8","Windows-1251",:invalid=>:replace,:undef=>:replace,:replace=>""
ec2 = Encoding::Converter.new "Windows-1251","UTF-8",:invalid=>:replace,:undef=>:replace,:replace=>""
t = ec2.convert ec1.convert t

1

जबकि नेकीलोन का समाधान काम करता है, कम से कम जहां तक ​​त्रुटि अतीत में है, मेरे मामले में, मेरे पास यह अजीब एफ-एड चरित्र था जो माइक्रोसॉफ्ट एक्सेल से सीएसवी में परिवर्तित हो गया था जो रूबी में पंजीकरण कर रहा था (इसे प्राप्त करें) साइरिल के। माणिक एक निर्भीक के थे। इसे ठीक करने के लिए मैंने 'iso-8859-1' का उपयोग किया। CSV.parse(f, :encoding => "iso-8859-1"), जो मेरे अजीब डरपोक सिरिलिक के बहुत अधिक प्रबंधनीय में बदल गया /\xCA/, जिसे मैं तब हटा सकता थाstring.gsub!(/\xCA/, '')


फिर से, मैं सिर्फ यह नोट करना चाहता हूं कि जब नकीलोन (और अन्य) फिक्स साइरिलिक पात्रों से उत्पन्न हुए थे (हाहा) सिरिलिया, यह आउटपुट एक सीएसवी के लिए मानक आउटपुट है जो xls से परिवर्तित किया गया था!
बोल्डर_रुबी

0

उपयोग करने से पहले scan, सुनिश्चित करें कि अनुरोधित पृष्ठ का Content-Typeहेडर है text/html, क्योंकि उन चीजों के लिंक हो सकते हैं जो UTF-8 में एन्कोडेड नहीं हैं। यदि आप hrefकिसी <link>तत्व में किसी चीज़ को उठाते हैं तो पृष्ठ भी गैर- html हो सकता है । यह कैसे जांचें कि आप किस HTTP लाइब्रेरी का उपयोग कर रहे हैं, उस पर भिन्नता है। फिर, सुनिश्चित करें कि परिणाम केवल एससीआई String#ascii_only?(यूटीएफ -8 नहीं है क्योंकि एचटीएमएल केवल एएससीआई का उपयोग करने वाला है, संस्थाओं को अन्यथा उपयोग किया जा सकता है)। यदि वे दोनों परीक्षण पास हो जाते हैं, तो इसका उपयोग करना सुरक्षित है scan


धन्यवाद, लेकिन यह मेरी समस्या नहीं है :) मैं केवल वैसे भी URL के होस्ट भाग को निकालता हूं और केवल फ्रंट पेज को हिट करता हूं। मेरी समस्या यह है कि मेरा इनपुट जाहिरा तौर पर UTF-8 नहीं है और 1.9 एन्कोडिंग फू हैयरवेट जाता है
मार्क सीजर

@ मर्क सीजर: "मेरे इनपुट" से आपका क्या तात्पर्य है? Stdin, URL या पेज बॉडी?
एड्रियन

HTML को UTF-8 में एन्कोड किया जा सकता है: en.wikipedia.org/wiki/Character_encodings_in_HTML
Eduardo

मेरा इनपुट = पेज बॉडी @Eduardo: मुझे पता है। मेरी समस्या यह है कि नेट / http से आने वाले डेटा को समय
मार्क सीगर

यह वास्तव में वास्तविक के लिए खराब एन्कोडिंग वेबपेजों के लिए असामान्य नहीं है। प्रतिक्रिया हैडर कह सकता है कि यह एक एन्कोडिंग है, लेकिन फिर वास्तव में एक और एन्कोडिंग की सेवा है।
सूर्यकिरण

-1

यदि आप डेटा के बारे में "परवाह" नहीं करते हैं तो आप कुछ ऐसा कर सकते हैं:

search_params = params[:search].valid_encoding? ? params[:search].gsub(/\W+/, '') : "nothing"

मैं तो बस पास हो जाता valid_encoding?था। मेरा एक खोज क्षेत्र है, और इसलिए मुझे बार-बार एक ही तरह की अजीबता महसूस हो रही थी इसलिए मैंने कुछ इस तरह का उपयोग किया: बस सिस्टम को तोड़ने के लिए नहीं। चूँकि मैं इस जानकारी को भेजने से पहले स्वतः अनुभव को नियंत्रित करने के लिए उपयोगकर्ता के अनुभव को नियंत्रित नहीं करता (जैसे कि "डमी अप!" कहने के लिए ऑटो फीडबैक) मैं इसे बस ले सकता हूं, इसे बाहर निकाल सकता हूं और खाली परिणाम लौटा सकता हूं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.