Frage String # encodiert den Fehler "ungültige Bytefolge in UTF-8" nicht


Ich weiß, dass es viele ähnliche Fragen zu diesem Fehler gibt, und ich habe viele von ihnen ohne Glück versucht. Das Problem, das ich habe, betrifft das Byte \xA1 und wirft

ArgumentError: Ungültige Bytefolge in UTF-8

Ich habe folgendes ohne Erfolg versucht:

"\xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").sub('', '')
"\xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").force_encoding('UTF-8').sub('', '')
"\xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").encode('UTF-8').sub('', '')

Jede Zeile wirft den Fehler für mich auf. Was mache ich falsch?

AKTUALISIEREN:

Die obigen Zeilen schlagen nur im IRB fehl. Allerdings habe ich meine Anwendung geändert, um Zeilen einer CVS-Datei mit der gleichen String # encode-Methode und Argumenten zu codieren, und ich bekomme den gleichen Fehler beim Lesen der Zeile von a Datei (Hinweis: Es funktioniert, wenn Sie die Operationen in derselben Zeichenfolge ausführen, ohne IO zu verwenden).

bad_line = "col1\tcol2\tbad\xa1"

bad_line.sub('', '') # does NOT fail
puts bad_line # => col1 col2    bad?

tmp = Tempfile.new 'foo' # write the line to a file to emulate real problem
tmp.puts bad_line
tmp.close

tmp2 = Tempfile.new 'bar'

begin
  IO.foreach tmp.path do |line|
    line.encode!('UTF-8', :undef => :replace, :invalid => :replace, :replace => "")
    line.sub('', '') # fail: invalid byte sequence in UTF-8
    tmp2.puts line
  end
  tmp2.close

  # this would fail if the above error didn't halt execution
  CSV.foreach(tmp2.path) do |row|
    puts row.inspect # fail: invalid byte sequence in UTF-8
  end
ensure
  tmp.unlink
  tmp2.close
  tmp2.unlink
end

11
2017-07-07 13:20


Ursprung


Antworten:


Es scheint, dass Ruby denkt, dass die String-Codierung bereits utf8 ist, also wenn Sie es tun

line.encode!('UTF-8', :undef => :replace, :invalid => :replace, :replace => "")

Es macht eigentlich nichts, weil die Zielkodierung die gleiche wie die aktuelle Kodierung ist (zumindest ist das meine Interpretation des Codes in transcode.c)

Die eigentliche Frage ist hier, ob Ihre Startdaten in einer Codierung gültig sind, die nicht utf-8 ist oder ob es sich um Daten handelt, die utf-8 sein sollen, aber einige Warzen darin enthalten sind, die Sie verwerfen möchten.

Im ersten Fall ist es das Richtige, Ruby zu sagen, was diese Kodierung ist. Sie können dies tun, wenn Sie die Datei öffnen

File.open('somefile', 'r:iso-8859-1')

öffnet die Datei und interpretiert ihren Inhalt als iso-8859-1

Sie können sogar Ruby für Sie transkodieren

File.open('somefile', 'r:iso-8859-1:utf-8')

öffnet die Datei als iso-8859-1, aber wenn Sie Daten daraus lesen, werden die Bytes für Sie in utf-8 konvertiert.

Sie können auch anrufen force_encoding um Ruby zu sagen, was die Kodierung einer Zeichenkette ist (dies verändert die Bytes überhaupt nicht, es teilt Ruby nur mit, wie sie zu interpretieren sind).

Im zweiten Fall, wo du einfach nur dreckigen Zeug wegwerfen willst, kannst du nicht einfach anrufen encode! wie du hast, denn das ist ein No-Op. In Ruby 2.1 und höher können Sie verwenden Zeichenfolge # scrubIn früheren Versionen können Sie dies tun

line.encode!('UTF-16', :undef => :replace, :invalid => :replace, :replace => "")
line.encode!('UTF-8')

Wir konvertieren zuerst zu utf-16. Da dies eine andere Kodierung ist, wird Ruby unsere ungültigen Sequenzen ersetzen. Wir können dann zurück zu utf-8 konvertieren. Dies wird uns keine zusätzlichen Daten verlieren, da utf-8 und utf-16 nur zwei verschiedene Arten sind, denselben zugrunde liegenden Zeichensatz zu codieren.


30
2017-07-07 16:37



Vielleicht führen Sie diesen Code in IRB aus. Ich habe viele Probleme mit der Codierung mit IRB gehabt. Versuchen Sie in diesem Fall, diesen Code als a zu speichern .rb Datei und führen Sie den Code von der Befehlszeile aus.


2
2017-07-07 13:43