2013-10-03 21 views
8

Aby przekonwertować ciąg na UTF-8 i zastąpić wszystkie błędy kodowania, można zrobić:Jak mogę zamienić błędy w UTF-8 w Ruby bez konwersji na inne kodowanie?

str.encode('utf-8', :invalid=>:replace) 

Jedyny problem polega na tym, że nie działa, jeśli jest już str UTF-8, w którym sprawa jakieś błędy pozostają:

irb> x = "foo\x92bar".encode('utf-8', :invalid=>:replace) 
=> "foo\x92bar" 
irb> x.valid_encoding? 
=> false 

zacytować Ruby Docs:

Należy pamiętać, że konwersja z kodowaniem enc do S kodowanie ame enc jest operacją "no-op", tzn. odbiornik jest zwracany bez żadnych zmian i nie są zgłaszane żadne wyjątki, nawet jeśli są niepoprawne bajty.

Oczywistym rozwiązaniem jest najpierw przekonwertować do innego kodowania Unicode, a następnie z powrotem na UTF-8

str.encode('utf-16', :invalid=>:replace).encode('utf-8') 

Na przykład:

irb> x = "foo\x92bar".encode('utf-16', :invalid=>:replace).encode('utf-8') 
=> "foo�bar" 
irb> x.valid_encoding? 
=> true 

Czy istnieje lepszy sposób to zrobić to bez konwersji na fałszywe kodowanie?

Odpowiedz

11

Ruby 2.1 wprowadził metodę String#scrub że robi to, co chcesz:

2.1.0dev :001 > x = "foo\x92bar" 
=> "foo\x92bar" 
2.1.0dev :002 > x.valid_encoding? 
=> false 
2.1.0dev :003 > y = x.scrub 
=> "foo�bar" 
2.1.0dev :004 > y.valid_encoding? 
=> true 

Ten sam popełnić także zmienia zachowanie encode tak, aby działało, gdy kodowanie źródłowe i dest są takie same:

2.1.0dev :005 > x = "foo\x92bar".encode('utf-8', :invalid=>:replace) 
=> "foo�bar" 
2.1.0dev :006 > x.valid_encoding? 
=> true 

O ile wiem, nie ma zbudowany w sposób to zrobić przed 2.1 (inaczej scrub nie być potrzebne), więc trzeba użyć trochę techniki obejście aż 2.1 wydany i możesz uaktualnić.

+0

Dzięki za informacje! Niestety, utknąłem z Ruby 1.9 i nie będę mógł dokonać aktualizacji (przynajmniej na ten projekt). – Matt

+1

Użyj można użyć klej 'scrub_rb' we wcześniejszych wersjach Ruby –

2

Spróbuj tego:

"foo\x92bar".chars.select(&:valid_encoding?).join 
    # => "foobar" 

lub wymienić

"foo\x92bar".chars.map{|c| c.valid_encoding? ? c : "?"}.join 
# => "foo?bar" 
+0

To także wydaje się być całkiem pracowite. – Matt

+0

@Matt nie, dokładnie tego chcesz. Znajdowanie i usuwanie nieprawidłowych znaków. – Reactormonk

+0

@Tass Jest różnica między "robieniem tego, co chcę robić" i "robieniem tego we właściwy sposób". Tak, robi to, co chcę, ale podobnie jak mój przykład powyżej z konwersją UTF-16. Miałem nadzieję, że zrobię to w sposób wbudowany, co najprawdopodobniej będzie bardziej wydajne niż sam proces iteracji. Przyznaję jednak, że jest to lepsze rozwiązanie niż to, które miałem. – Matt