루비 1.9 : UTF-8에서 유효하지 않은 바이트 시퀀스


109

많은 임의의 사이트에서 많은 HTML을 소비하는 Ruby (1.9)로 크롤러를 작성하고 있습니다.
링크를 추출하려고 할 때 .scan(/href="(.*?)"/i)nokogiri / hpricot 대신 사용하기로 결정했습니다 (주요 속도 향상). 문제는 이제 " invalid byte sequence in UTF-8"오류 가 많이 발생한다는 것 입니다.
내가 이해 한 바에 따르면 net/http라이브러리에는 인코딩 특정 옵션이 없으며 제공되는 항목은 기본적으로 제대로 태그가 지정되지 않았습니다.
들어오는 데이터로 실제로 작업하는 가장 좋은 방법은 무엇입니까? .encode교체 및 유효하지 않은 옵션 세트로 시도했지만 지금까지 성공하지 못했습니다 ...


문자를 깨뜨릴 수 있지만 다른 라이브러리에 유효한 문자열을 유지하는 것 : valid_string = untrusted_string.unpack ( 'C *'). pack ( 'U *')
Marc Seeger

정확한 문제가 있으면 동일한 다른 솔루션을 시도했습니다. 사랑이 없습니다. Marc 's를 시도했지만 모든 것을 왜곡하는 것 같습니다. 당신은 확실 'U*'상태 해제를 'C*'?
Jordan Feldstein 2011 년

아니요, 그렇지 않습니다 :) 여기저기서 문장에 대해하는 것보다 더 많이 충돌하지 않는 타사 라이브러리에 관심이있는 웹 크롤러에서 사용했습니다.
Marc Seeger 2012

답변:


172

Ruby 1.9.3에서는 String.encode를 사용하여 유효하지 않은 UTF-8 시퀀스를 "무시"할 수 있습니다. 다음은 1.8 ( iconv ) 및 1.9 ( String # encode ) 모두에서 작동하는 스 니펫입니다 .

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')
RubenLaguna

7
의 옵션도 force_encoding있습니다. ISO8859-1을 UTF-8로 읽은 경우 (따라서 해당 문자열에 유효하지 않은 UTF-8이 포함되어 있음) the_string.force_encoding ( "ISO8859-1")을 사용하여 ISO8859-1로 "재 해석"할 수 있습니다. 실제 인코딩에서 해당 문자열로.
RubenLaguna 2012

3
이중 인코딩 트릭이 방금 내 베이컨을 구했습니다! 그래도 왜 필요한지 궁금합니다.
johnf

1
그 라인을 어디에 두어야합니까?
Lefsler 2012-08-30

5
이중 변환은 인코딩 변환을 강제하기 때문에 작동한다고 생각합니다 (그리고 유효하지 않은 문자 확인). 소스 문자열이 이미 UTF-8로 인코딩 된 경우 호출 .encode('UTF-8')은 작동하지 않으며 검사가 실행되지 않습니다. 인코딩을위한 Ruby Core 문서 . 그러나 UTF-16으로 변환하면 먼저 유효하지 않은 바이트 시퀀스에 대한 모든 검사가 강제 실행되고 필요에 따라 교체가 수행됩니다.
Jo Hund

79

받아 들인 대답이나 다른 대답이 저에게 효과적입니다. 제안한 이 게시물 을 찾았 습니다.

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

이것은 나를 위해 문제를 해결했습니다.


1
이것은 나를 위해 문제를 해결했으며 더 이상 사용되지 않는 방법을 사용하는 것을 좋아합니다 (현재 Ruby 2.0이 있습니다).
La-comadreja 2014

1
이것은 작동하는 유일한 사람입니다! 위의 솔루션을 모두 시도해 보았지만 "fdsfdsf dfsf sfds fs sdf <div> hello <p> fooo ??? {! @ # $ % ^ & * () _ +} < / p> </ div> \ xEF \ xBF \ xBD \ xef \ xbf \ x9c <div> \ xc2 \ x90 </ div> \ xc2 \ x90 "
Chihung Yu

1
두 번째 인수 '이진'은 무엇입니까?
Henley Chiu

24

내 현재 솔루션은 다음을 실행하는 것입니다.

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

이것은 적어도 내 주요 문제였던 예외를 제거합니다.


3
나는 valid_encoding?무언가가 잘못되었을 때 감지하는 것처럼 보이는 이 방법을 함께 사용 하고 있습니다. val.unpack('C*').pack('U*') if !val.valid_encoding?.
Aaron Gibralter

이것은 나를 위해 일했습니다. 내 \xB0등을 학위 기호 로 성공적으로 변환 합니다. 심지어 valid_encoding?다시 사실로 돌아 왔지만 여전히 그렇지 않은지 확인하고 위의 Amir의 대답을 사용하여 문제가되는 문자를 제거합니다 string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: ''). 나는 또한 force_encoding경로를 시도 했지만 실패했습니다.
hamstar 2014-08-04

이것은 훌륭합니다. 감사.
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

내 사건에 대한 최고의 답변! 감사합니다
Aldo

4

HTML 파서를 사용하는 것이 좋습니다. 가장 빠른 것을 찾으십시오.

HTML 구문 분석은 생각만큼 쉽지 않습니다.

브라우저는 " "기호 만 넣어서 UTF-8 HTML 문서에서 잘못된 UTF-8 시퀀스를 구문 분석합니다. 따라서 HTML의 유효하지 않은 UTF-8 시퀀스가 ​​구문 분석되면 결과 텍스트는 유효한 문자열입니다.

속성 값 내에서도 amp와 같은 HTML 엔티티를 디코딩해야합니다.

다음은 정규 표현식으로 HTML을 안정적으로 구문 분석 할 수없는 이유를 요약 한 훌륭한 질문입니다. RegEx는 XHTML 자체 포함 태그를 제외한 열린 태그와 일치합니다.


2
약 10 배 더 빠르기 때문에 정규 표현식을 유지하고 싶습니다. HTML을 올바르게 구문 분석하고 싶지는 않지만 링크를 추출하고 싶습니다. ok_string = bad_string.encode ( "UTF-8", {: invalid => : replace, : undef => : replace})를 수행하여 루비의 유효하지 않은 부분을 교체 할 수 있어야합니다. 작업 :(
Marc Seeger

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

Nakilon의 솔루션이 작동하는 동안 적어도 오류가 발생하는 한, 필자의 경우 Microsoft Excel에서 생성 된이 이상한 f-ed up 문자를 ruby에 등록한 CSV로 변환하여 (get this) cyrillic K로 변환했습니다. 루비는 굵은 K였습니다.이 문제를 해결하기 위해 'iso-8859-1'즉, CSV.parse(f, :encoding => "iso-8859-1"), 내 기묘한 deaky 키릴 문자 K를 훨씬 더 관리하기 쉽게 만들었고 /\xCA/, 다음으로 제거 할 수있었습니다.string.gsub!(/\xCA/, '')


다시 말하지만, Nakilon (및 기타) 수정 사항은 (haha) Cyrillia에서 시작된 키릴 문자에 대한 것이지만이 출력은 xls에서 변환 된 csv에 대한 표준 출력입니다!
boulder_ruby 2010 년

0

를 사용하기 전에 scan요청 된 페이지의 Content-Type헤더가 인지 확인하세요 text/html. UTF-8로 인코딩되지 않은 이미지와 같은 항목에 대한 링크가있을 수 있기 때문입니다. 요소 href와 같은 것을 선택하면 페이지가 HTML이 아닐 수도 있습니다 <link>. 이를 확인하는 방법은 사용중인 HTTP 라이브러리에 따라 다릅니다. 그런 다음 결과가 ascii 만 있는지 확인합니다 String#ascii_only?(HTML은 ascii 만 사용해야하므로 UTF-8이 아님, 엔터티는 그렇지 않으면 사용할 수 있음). 이 두 테스트가 모두 통과하면 scan.


고마워,하지만 그건 내 문제가 아니야 :) 어쨌든 URL의 호스트 부분 만 추출하고 첫 페이지 만 쳤다. 내 문제는 분명히 내 입력이 UTF-8이 아니고 1.9 인코딩 foo가 문제가된다는 것입니다
Marc Seeger

@Marc Seeger : "내 입력"이란 무엇을 의미합니까? Stdin, URL 또는 페이지 본문?
Adrian

HTML은 UTF-8로 인코딩 할 수 있습니다. en.wikipedia.org/wiki/Character_encodings_in_HTML
Eduardo

내 입력 = 페이지 본문 @Eduardo : 알아요. 내 문제는 net / http에서 오는 데이터가 때때로 잘못된 인코딩으로 보이는 것입니다
Marc Seeger

웹 페이지가 실제로 실제로 잘못된 인코딩을 갖는 것은 드문 일이 아닙니다. 응답 헤더는 하나의 인코딩이라고 말하지만 실제로는 다른 인코딩을 제공합니다.
sunkencity

-1

데이터에 대해 "관심"하지 않으면 다음과 같이 할 수 있습니다.

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

나는 valid_encoding?그것을 통과하기 위해 사용 했습니다. 내 것은 검색 필드이고, 그래서 나는 같은 이상한 점을 계속해서 찾아 내서 다음과 같은 것을 사용했습니다 : 단지 시스템이 고장 나지 않게하기 위해서. 이 정보를 보내기 전에 자동 유효성 검사를 수행하는 사용자 환경을 제어하지 않기 때문에 (예 : "더미!"라고 말하는 자동 피드백) 정보를 가져 와서 제거하고 빈 결과를 반환 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.