2013-06-07 3 views
17

Ich habe eine Rails-Anwendung von Migrationen seit Rails Version 1 und ich würde gerne ignorieren alle ungültige Byte-Sequenzen auf, um die Abwärtskompatibilität zu erhalten.Wie kann ich ungültige Bytefolgen in UTF-8-Strings global ignorieren?

Ich kann die Eingabe Codierung nicht kennen.

Exemple:

> "- Men\xFC -".split("n") 
ArgumentError: invalid byte sequence in UTF-8 
    from (irb):4:in `split' 
    from (irb):4 
    from /home/fotanus/.rvm/rubies/ruby-2.0.0-rc2/bin/irb:16:in `<main>' 

ich dieses Problem in einer Linie überwinden, indem Sie die folgende Verwendung, zum Beispiel:

> "- Men\xFC -".unpack("C*").pack("U*").split("n") 
=> ["- Me", "ü -"] 

aber ich möchte immer die ungültigen Byte-Sequenzen ignorieren und deaktivieren diese Fehler. Auf Ruby selbst oder in Rails.

+1

Zeigen Sie einige Beispiele der ungültigen Daten an. Wie lautet die Codierung in Ihrer Datenbank oder in Ihren Tabellen? Rails muss dem entsprechen. Data Rails empfängt muss zu der gleichen Codierung gezwungen werden, die die Datenbank speichert, andernfalls müssen Sie die binäre <--> ASCII- oder binäre <--> UTF-8-Codierung verwenden. –

+1

@fotanus: Es hat mit Ruby 1.8 funktioniert, weil Ruby 1.8 die Codierung nicht auf die gleiche Weise behandelt hat (tatsächlich überhaupt). Siehe z.B.http://yokolet.blogspot.com/2009/07/design-and-implementation-of-ruby-m17n.html und http://yehudakatz.com/2010/05/05/ruby-1-9-encodings- a-primer-and-the-solution-for-rails/ –

+0

@Denis, ich bin mir bewusst, dass es sich geändert hat, deshalb kämpfe ich gegen dieses Problem. – fotanus

Antwort

15

Ich glaube nicht, Sie global ohne große Schwierigkeiten die UTF-8 Überprüfung ausschalten. Ich konzentriere mich stattdessen darauf, alle Zeichenfolgen, die in Ihre Anwendung eintreten, an der Grenze zu korrigieren, an der sie eingehen (z. B. wenn Sie die Datenbank abfragen oder HTTP-Anfragen erhalten).

Angenommen, die eingehenden Strings haben die BINARY-Codierung (ASCII-8BIT-Codierung). Dies kann wie folgt simuliert werden:

s = "Men\xFC".force_encoding('BINARY') # => "Men\xFC" 

Dann haben wir sie in UTF-8 konvertieren können mit String#encode und ersetzen Sie alle undefinierten Zeichen mit den UTF-8-Ersatzzeichen:

s = s.encode("UTF-8", invalid: :replace, undef: :replace) # => "Men\uFFFD" 
s.valid_encoding? # => true 

Leider sind die oben genannten Schritte würde am Ende eine Menge von UTF-8-Codepunkten mangeln, weil die Bytes in ihnen nicht erkannt würden. Wenn Sie ein 3-Byte-UTF-8-Zeichen wie "\ uFFFD" hätten, würde es als drei separate Bytes interpretiert werden und jedes würde in das Ersatzzeichen umgewandelt werden. Vielleicht könnten Sie so etwas tun:

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

Das ist das Beste, was ich mir vorstellen konnte. Leider weiß ich nicht, wie Ruby die Zeichenfolge als UTF-8 behandeln und alle ungültigen Bytes ersetzen soll.

+1

Danke für Ihre Antwort, es ist das nächste einer globalen Lösung. Sie können String-Methoden immer neu definieren, um dies zu tun, aber ich denke, ich werde Aufgeben und den ganzen Code behandeln, der von Fall zu Fall geht, denn das Hinzufügen zu String wäre sehr hackish. – fotanus

3

Wenn Sie nur mit den rohen Bytes arbeiten möchten, können Sie versuchen, es als ASCII-8BIT/BINARY zu kodieren.

str.force_encoding("BINARY").split("n") 

Dies ist nicht Ihre ü zurück aber in Gang zu bringen, da Ihre Quellzeichenfolge in diesem Fall ist ISO-8859-1 (oder so ähnlich):

"- Men\xFC -".force_encoding("ISO-8859-1").encode("UTF-8") 
=> "- Menü -" 

Wenn Sie möchten, Um Multibyte-Zeichen zu erhalten, müssen Sie wissen, was der Quellzeichensatz ist. Sobald Sie force_encoding zu BINARY, werden Sie buchstäblich nur die rohen Bytes haben, so Multibyte-Zeichen werden nicht entsprechend interpretiert werden.

Wenn die Daten aus Ihrer Datenbank stammen, können Sie den Verbindungsmechanismus ändern, um eine ASCII-8BIT- oder BINARY-Codierung zu verwenden. Ruby sollte dann entsprechend kennzeichnen. Alternativ können Sie den Datenbanktreiber so einstellen, dass die Codierung für alle gelesenen Zeichenfolgen erzwungen wird. Dies ist jedoch ein massiver Hammer und könnte absolut falsch sein.

Die richtige Antwort wird sein, Ihre String-Codierungen zu beheben. Dies kann eine Datenbankkorrektur, eine Datenbanktreiber-Verbindungscodierungskorrektur oder eine Kombination daraus erfordern. Alle Bytes sind noch da, aber wenn Sie mit einem bestimmten Zeichensatz zu tun haben, sollten Sie, wenn irgend möglich, Ruby wissen lassen, dass Sie erwarten, dass Ihre Daten in dieser Kodierung sind. Ein häufiger Fehler besteht darin, den mysql2-Treiber zu verwenden, um eine Verbindung zu einer MySQL-Datenbank herzustellen, die Daten in latin1-Kodierungen enthält, aber einen utf-8-Zeichensatz für die Verbindung anzugeben. Dies führt dazu, dass Rails die latin1-Daten aus der DB übernimmt und sie als utf-8 interpretiert, anstatt sie als latin1 zu interpretieren, die Sie dann in UTF-8 konvertieren können.

Wenn Sie näher erläutern können, woher die Strings kommen, ist möglicherweise eine vollständigere Antwort möglich. Sie können auch this answer für eine mögliche globale (-ish) Rails-Lösung für Standard-Zeichenkodierungen auschecken.

2

Wenn Sie Ihre Datenbank/Seite/was auch immer konfigurieren können, um Ihnen Strings in ASCII-8BIT zu geben, erhalten Sie ihre echte Kodierung.

Verwenden Sie Rubys Stdlib-Encoding-Rate-Bibliothek. Führen Sie alle Ihre Saiten durch etwas wie folgt aus:

require 'nkf' 
str = "- Men\xFC -" 
str.force_encoding(NKF.guess(str)) 

Die NKF Bibliothek wird die Codierung (in der Regel erfolgreich) erraten, und zwingen diese Codierung auf der Saite.Wenn Sie die NKF Bibliothek völlig nicht das Gefühl, wie vertrauensvoll, baut diesen Schutz um String-Operationen zu:

begin 
    str.split 
rescue ArgumentError 
    str.force_encoding('BINARY') 
    retry 
end 

Diese auf BINARY Rückfall, wenn NKF nicht richtig erraten hat. Sie können diese in eine Methode Wrapper drehen:

def str_op(s) 
    begin 
    yield s 
    rescue ArgumentError 
    s.force_encoding('BINARY') 
    retry 
    end 
end 
6

in Ruby 2.0 Sie den String # b-Methode verwenden könnte, das ist eine kurze Alias ​​String # force_encoding ("binary")

+1

Dies ist -sehr informativ, und ich bin glücklich mit den Informationen, aber vielleicht passt es besser als ein Kommentar? – fotanus

1

Encoding in Ruby 1.9 und 2.0 scheint ein bisschen schwierig zu sein. \ xFC ist der Code für das Sonderzeichen ü in ISO-8859-1, aber der Code FC tritt auch in UTF-8 für ü U+00FC = \u0252 (und in UTF-16) auf. Es könnte ein Artefakt des Rubins pack/unpack functions sein. Packen und Entpacken von Unicode-Zeichen mit dem U * Template-String für Unicode ist nicht problematisch:

>> "- Menü -".unpack('U*').pack("U*") 
=> "- Menü -" 

Sie können die „falsche“ Zeichenfolge erstellen, dh eine Zeichenfolge, die eine ungültige Codierung hat, wenn Sie zuerst Unicode UTF-8 entpacken Zeichen (U), und dann packen unsigned Zeichen (C):

>> "- Menü -".unpack('U*').pack("C*") 
=> "- Men\xFC -" 

Diese Saite hat nicht länger eine gültige Codierung. Offenbar kann der Umwandlungsprozess durch die Anwendung der umgekehrte Reihenfolge (ein bisschen wie Operatoren in der Quantenphysik) rückgängig gemacht werden:

>> "- Menü -".unpack('U*').pack("C*").unpack("C*").pack("U*") 
=> "- Menü -" 

In diesem Fall ist es auch möglich, die gerissene Saite „zu reparieren“, indem man zuerst Umwandlung in ISO- 8859-1 und dann zu UTF-8, aber ich bin mir nicht sicher, ob das versehentlich funktioniert, weil der Code in diesem Zeichensatz enthalten ist

>> "- Men\xFC -".force_encoding("ISO-8859-1").encode("UTF-8") 
=> "- Menü -" 
>> "- Men\xFC -".encode("UTF-8", 'ISO-8859-1') 
=> "- Menü -" 
+0

Interessanter Beitrag, aber beantwortet nicht wirklich die Frage - es ist keine globale Lösung, es ist nur für eine Zeichenfolge – fotanus

+0

Ja, wahrscheinlich.Ich habe ein ähnliches Problem, wo Ihre ungültige Zeichenfolge mit dem \ xFC-Zeichen Ich hatte eine UTF-8-kodierte Textdatei mit Sonderzeichen wie ä, ö, ü und irgendwie gab File.open ungültige Zeichenfolgen zurück, obwohl die Kodierung korrekt als UTF-8 erkannt wurde :-( – 0x4a6f4672

+0

Meine Zeichenfolgen stammen vom schlechtesten Ort: eine vom Benutzer hochgeladene Datei. – fotanus

Verwandte Themen