代码之家  ›  专栏  ›  技术社区  ›  Marc Seeger

Ruby1.9:UTF-8中的字节序列无效

  •  106
  • Marc Seeger  · 技术社区  · 15 年前

    我正在用Ruby(1.9)编写一个爬虫程序,它会消耗大量随机站点的HTML。
    当尝试提取链接时,我决定只使用 .scan(/href="(.*?)"/i) 而不是Nokogiri/hpricot(主要加速)。问题是我现在收到了很多 invalid byte sequence in UTF-8 “错误。
    据我所知, net/http 库没有任何特定于编码的选项,并且输入的内容基本上没有正确的标记。
    实际处理这些传入数据的最佳方法是什么?我试过 .encode 设置了替换和无效选项,但到目前为止没有成功…

    11 回复  |  直到 9 年前
        1
  •  170
  •   Mark Swardstrom    11 年前

    在Ruby1.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
    
        2
  •  77
  •   Amir Raminfar Hadi Rasouli    9 年前

    被接受的答案和另一个答案对我都不起作用。我发现 this post 这建议

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

    这帮我解决了问题。

        3
  •  23
  •   Marc Seeger    13 年前

    我当前的解决方案是运行:

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

    这至少能消除我的主要问题

        4
  •  8
  •   Ranjithkumar Ravi    11 年前

    试试这个:

    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
    
        5
  •  4
  •   Community CDub    8 年前

    我建议您使用HTML解析器。找一个最快的。

    解析HTML并不像看上去那么容易。

    浏览器解析无效的UTF-8序列,在UTF-8 HTML文档中,只需放置“_?.5”符号。所以一旦HTML中无效的UTF-8序列被解析,结果文本就是一个有效的字符串。

    即使在属性值内,您也必须解码HTML实体,如AMP

    下面是一个很好的问题,总结了为什么不能用正则表达式可靠地解析HTML: RegEx match open tags except XHTML self-contained tags

        6
  •  3
  •   Spajus    12 年前

    这似乎有效:

    def sanitize_utf8(string)
      return nil if string.nil?
      return string if string.valid_encoding?
      string.chars.select { |c| c.valid_encoding? }.join
    end
    
        7
  •  3
  •   rusllonrails    11 年前
    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
    
        8
  •  2
  •   Nakilon earlonrails    13 年前

    我遇到了字符串,它混合了英语、俄语和其他一些字母,这导致了异常。我只需要俄语和英语,目前这对我很有用:

    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
    
        9
  •  1
  •   boulder_ruby    12 年前

    虽然Nakilon的解决方案有效,但至少在克服错误的过程中,在我的例子中,我有一个奇怪的f-ed up字符,源于Microsoft Excel,它被转换为csv,在ruby中注册为(get this)西里尔字母k,在ruby中是一个粗体k。为了解决这个问题,我使用了“iso-8859-1”即。 CSV.parse(f, :encoding => "iso-8859-1") 把我那古怪的、震耳欲聋的西里尔字母K变成了一个更容易管理的词。 /\xCA/ ,然后我可以移除 string.gsub!(/\xCA/, '')

        10
  •  0
  •   Adrian    15 年前

    在使用之前 scan ,确保请求的页面 Content-Type 页眉是 text/html ,因为可以链接到图像之类的东西,而这些图像不是用UTF-8编码的。如果您选择了 href 有点像 <link> 元素。如何检查它取决于您使用的HTTP库。然后,确保结果仅为带 String#ascii_only? (不是UTF-8,因为HTML只应该使用ASCII,实体可以用其他方式)。如果这两个测试都通过了,就可以安全地使用 扫描 .

        11
  •  -1
  •   pjammer    11 年前

    如果你不关心数据,你可以做如下的事情:

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

    我刚刚用过 valid_encoding? 为了通过它。我的是一个搜索字段,所以我一次又一次地发现了同样的奇怪,所以我使用了类似的东西:只是为了让系统不被破坏。因为我不控制用户体验在发送此信息之前自动验证(比如自动反馈说“dummup!”)我可以把它拿进去,去掉,然后返回空白结果。