Estoy escribiendo un rastreador en Rubí (1.9) que consume un montón de HTML a partir de un montón de sitios al azar.

Cuando se trata de extraer los enlaces, decidí utilizar .scan(/href="(.*?)"/i) en lugar de nokogiri/hpricot (mayor rapidez). El problema es que yo ahora a recibir un montón de «invalid byte sequence in UTF-8» errores.

De lo que he entendido, el net/http biblioteca no tiene ningún tipo de codificación de las opciones específicas y las cosas que viene en es, básicamente, no están adecuadamente etiquetados.

¿Cuál sería la mejor manera de trabajar con los datos de entrada? Traté de .encode con la reemplace y no válidos del conjunto de opciones, pero hasta ahora sin éxito…

  • algo que se puede romper personajes, pero mantiene la cadena válida para otras bibliotecas: valid_string = untrusted_string.desempaquetar(‘C*’).pack(‘U*’)
  • Tener el problema exacto, tratado de la misma de otras soluciones. No hay amor. Trató de Marc, pero parece tergiversar todo. Está usted seguro de 'U*' deshace 'C*'?
  • No, no 🙂 acabo de utilizar que en un webcrawler donde me preocupo por la 3ª parte de las bibliotecas de no chocar más que yo acerca de una frase aquí y allá.
InformationsquelleAutor Marc Seeger | 2010-06-06

11 Comentarios

  1. 172

    En Ruby 1.9.3 es posible utilizar la Cadena.codificar a «ignorar» el inválido UTF-8 secuencias. Aquí es un fragmento de código que funcionará tanto en 1.8 (iconv) y 1.9 (Cadena#codificar) :

    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

    o si usted tiene realmente molesto de entrada se puede hacer una doble conversión de UTF-8, UTF-16 y de vuelta a 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
    • He comparado con mi solución y se encontró, que el mío pierde algunas de las letras, al menos ё: "Alena V.\". Mientras que la solución se mantiene: "Ale\u0308na V.\". De niza.
    • Con algunos problemas de entrada yo también uso una doble conversión de UTF-8, UTF-16 y luego de vuelta a UTF-8 file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '') file_contents.encode!('UTF-8', 'UTF-16')
    • También existe la opción de force_encoding. Si usted tiene una lectura de un ISO8859-1 como UTF-8 (y por lo tanto que la cadena contiene no válido UTF-8), puede «interpretar» como ISO8859-1 con the_string.force_encoding(«ISO8859-1») y sólo el trabajo con la cadena en su verdadera codificación.
    • Que doble codificar truco sólo salvado el pellejo! Me pregunto por qué es necesario aunque?
    • Estoy usando esta en mi base de datos mysql de Apple de afiliados de la alimentación para la app store de datos. El doble codificar funciona! Pero el formato en el app descripciones está en mal estado hasta ahora :/
    • tengo este código de error del convertidor no se encuentra (UTF-8, UTF-16)
    • Donde debo poner esas líneas?
    • Creo que el doble conversión funciona porque obliga a una conversión de codificación (y con él, la comprobación de que los caracteres no válidos). Si la cadena de origen ya está codificado en UTF-8, a continuación, simplemente llamando a la .encode('UTF-8') es un no-op, y no se verifica. Ruby Documentación de la base para codificar. Sin embargo, su conversión a UTF-16 primer fuerzas de todas las comprobaciones de la validez de las secuencias de bytes que se deben ejecutar y sustituciones se hará según sea necesario.
    • Si desea un ejemplo de cadena para la que la doble conversión es necesaria, aquí está uno que tengo URI.decode("%E2%EF%BF%BD%A6-invalid").

  2. 79

    La aceptada respuesta ni la otra respuesta a trabajar para mí. He encontrado este post que sugiere

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

    Esta solucionado el problema para mí.

    • Esto se ha solucionado el problema para mí y me gusta el uso de la no-deprecated métodos (tengo Ruby 2.0 ahora).
    • Este es el único que funciona! He probado todos los de la solución anterior, ninguno de ellos la Cadena de trabajo utilizado en la prueba de «fdsfdsf dfsf sfds fs sdf <div>hola<p>fooo??? {[email protected]#$%^&*()_+}</p></div> \xEF\xBF\xBD \xef\xbf\x9c <div>\xc2\x90</div> \xc2\x90»
    • ¿Cuál es el segundo argumento de ‘binario’ para?
  3. 23

    Mi solución actual es ejecutar:

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

    Eso al menos deshacerse de las excepciones, que era mi principal problema

    • Estoy usando este método en combinación con valid_encoding? que parece detectar cuando algo está mal. val.unpack('C*').pack('U*') if !val.valid_encoding?.
    • Este trabajó para mí. Convierte correctamente mi \xB0 de vuelta a grados símbolos. Incluso el valid_encoding? vuelve verdadera, pero todavía puedo comprobar si es que no, y tira de los infractores caracteres usando el Emir de la respuesta anterior: string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: ''). Yo también había probado el force_encoding ruta, pero que no se pudo.
    • Esto es genial. Gracias.
  4. 8

    Intente esto:

    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
    • Mejor respuesta para mi caso! Gracias
  5. 4

    Recomiendo el uso de un analizador de HTML. Acaba de encontrar el más rápido.

    Análisis de HTML no es tan fácil como puede parecer.

    Navegadores analizar UTF-8 no válida secuencias, en UTF-8 documentos HTML, simplemente poniendo el «�» símbolo. Así que una vez que el UTF-8 no válida secuencia en el HTML obtiene analiza el texto resultante es una cadena válida.

    Incluso dentro de los valores de atributo que tienen que decodificar entidades HTML como aplicaciones de

    Aquí es una gran pregunta que resume por qué no se puede analizar de forma fiable HTML con una expresión regular:
    RegEx partido abierto las etiquetas excepto XHTML autónomo etiquetas

    • Me encantaría mantener la regexp, ya que es aproximadamente 10 veces más rápido y yo realmente no quiero analizar el código html correctamente, pero sólo quiero extraer enlaces. Yo debería ser capaz de reemplazar las partes inválidas en ruby solo por hacer: ok_string = bad_string.encode(«UTF-8», {:invalid => :reemplazar :indef => :reemplazar}), pero que no parece funcionar 🙁
  6. 3

    Esto parece funcionar:

    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
    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

    Me he encontrado con cadena, que había mixings de inglés, ruso y algunos otros alfabetos, lo que provocó la excepción. Necesito solo el ruso y el inglés, y actualmente trabaja para mí:

    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

    Mientras Nakilon la solución funciona, al menos hasta que pasen el error, en mi caso, yo tenía esta extraña f-ed de caracteres originarios de Microsoft Excel convierte a CSV que se estaba registrando en ruby como (conseguir este) cirílico K, que en ruby era una negrita K. Para solucionar esto he utilizado ‘iso-8859-1’ viz. CSV.parse(f, :encoding => "iso-8859-1"), que se volvió mi freaky deaky cirílico K en una mucho más manejable /\xCA/, que podría, a continuación, retire con string.gsub!(/\xCA/, '')

    • De nuevo, sólo quiero señalar que mientras Nakilon (y otros) corrección de los caracteres Cirílicos procedentes de (jaja) Cyrillia, esta salida es la salida estándar de un archivo csv que se ha convertido desde xls!
  10. 0

    Antes de utilizar scan, asegúrese de que la página solicitada del Content-Type encabezado es text/html, ya que puede haber enlaces a cosas como imágenes que no están codificados en UTF-8. La página también podría ser que no sean html, si coges una href en algo así como un <link> elemento. Cómo comprobar esto varía en lo HTTP biblioteca está utilizando. A continuación, asegúrese de que el resultado es sólo ascii con String#ascii_only? (no UTF-8 ya que HTML es sólo supone el uso de ascii, las entidades pueden utilizarse de otra manera). Si ambas pruebas pasan, es seguro de usar scan.

    • gracias, pero ese no es mi problema 🙂 yo sólo extraer la parte de host de la URL de todos modos y golpear sólo la primera página. Mi problema es que mi entrada aparentemente no es UTF-8 y el 1.9 codificación de foo va de mal en peor
    • Seeger: ¿Qué quieres decir con «mi entrada»? Stdin, la dirección URL, o el cuerpo de la página?
    • HTML puede ser codificado en UTF-8: en.wikipedia.org/wiki/Character_encodings_in_HTML
    • mi entrada = el cuerpo de la página @Eduardo: lo sé. Mi problema es que los datos provenientes de net/http parece tener una mala codificación a partir de tiempo al tiempo
    • No es raro que las páginas web a los que realmente tienen la mala codificación real. El encabezado de respuesta podría decir que es uno de codificación, pero luego en realidad servir a otra codificación.
  11. -1

    Si no «preocuparse» por los datos que usted puede hacer algo como:

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

    Acabo de utilizar valid_encoding? para obtener pasó. El mío es un campo de búsqueda, y así me fue la búsqueda de la misma rareza más y más de lo que he usado algo como: sólo para tener el sistema no se rompa. Porque no puedo controlar la experiencia del usuario a autovalidate antes de enviar esta información (como el auto de retroalimentación para decir «dummy up!») Yo sólo se puede dar en, tira de él hacia fuera y devolución de resultados en blanco.

Dejar respuesta

Please enter your comment!
Please enter your name here