2009-08-28 3 views
52

Jestem w trakcie naprawiania złego kodowania UTF8. Obecnie używam PHP 5 i MySQLNaprawianie uszkodzonego kodowania UTF8

W mojej bazy danych mam kilka przypadków złych kodowań, które drukują jak: î

  • sortowanie bazy danych jest utf8_general_ci
  • PHP przy użyciu odpowiedniego nagłówka UTF8
  • Notepad ++ jest skonfigurowany do korzystania UTF8 bez BOM
  • zarządzania bazami danych jest obsługiwana w phpMyAdmin
  • nie we wszystkich przypadkach znaki akcentowane są łamane

Potrzebuję jakiejś funkcji, która pomoże mi zmapować wystąpienia "A", "A", "A" i innych podobnych do ich właściwych znaków akcentowanych w UTF8.

+0

Być może można wymienić postacie te mają reprezentować? A może zrzut heksadecymalny? – Managu

+6

Szybki wygląd sugeruje, że twoje struny mogły być "podwójnie" zakodowane w utf-8. To znaczy. zakodowane w utf-8, bajty te są traktowane jako znaki Unicode, a wynik zakodowany w utf-8. Idąc wstecz: "î" = "\ xC3 \ x83 \ xC2 \ xAE" <- (utf-8) - "\ xC3 \ xAE" <- (utf-8) - "\ xEE" = "î". A może nie - niewiele danych do zdiagnozowania tutaj. – Managu

+0

Możliwe, że był podwójnie zakodowany. Czy istnieje bezpieczny sposób programowego sprawdzenia tego, a jeśli tak, jaki jest najlepszy sposób bezpiecznego odkodowania podwójnego kodowania? – Jayrox

Odpowiedz

54

Musiałem spróbować "naprawić" wiele zepsutych sytuacji UTF8 w przeszłości, i niestety to nigdy nie jest łatwe, a często raczej niemożliwe.

Jeśli nie można dokładnie ustalić, jak został złamany, i zawsze był łamany w dokładnie taki sam sposób, trudno będzie "cofnąć" obrażenia.

Jeśli chcesz spróbować zniwelować obrażenia, najlepszym rozwiązaniem byłoby rozpoczęcie pisania przykładowego kodu, w którym spróbujesz różnych odmian połączeń z mb_convert_encoding(), aby sprawdzić, czy możesz znaleźć kombinację "od" i "to", które poprawia twoje dane. W końcu często lepiej nie martwić się o utrwalanie starych danych z powodu poziomu bólu, ale zamiast tego po prostu naprawiać rzeczy w przyszłości.

Jednak przed wykonaniem tej czynności należy się upewnić, że naprawiono wszystko, co powoduje ten problem. Wspomniałeś już, że sortowanie tabel DB i edytory są ustawione poprawnie.Ale istnieje więcej miejsc, gdzie trzeba sprawdzić, aby upewnić się, że wszystko jest prawidłowo UTF-8:

  • Upewnij się, że kod HTML służą jako UTF-8:
    • nagłówku ("Content Wpisz: text/html; charset = utf-8 ");
  • Zmiana domyślnego kodowania PHP na UTF-8:
    • ini_set ("default_charset", 'UTF-8');
  • Jeśli baza danych nie zawsze mówić w UTF-8, a następnie być może trzeba powiedzieć go na za połączenia podstawy, aby upewnić się, że jest w UTF-8 trybie, w MySQL to zrobić poprzez emisję:
    • charset utf8
  • może trzeba poinformować serwer WWW, aby zawsze starają się mówić w UTF-8, w Apache ta komenda jest:
    • AddDefaultCharset uTF8
  • Wreszcie, ZAWSZE upewnij się, że korzystasz z funkcji PHP, które są poprawnie reklamowane przez UTF-8. Oznacza to zawsze używanie funkcji ciągów znaków "wielobajtowych" w stylu mb_*. Oznacza to także, że podczas wywoływania funkcji, takich jak htmlspecialchars(), na końcu należy podać odpowiedni parametr zestawu znaków "utf-8", aby upewnić się, że nie koduje on niepoprawnie.

Jeśli nie zauważysz żadnego kroku przez cały proces, kodowanie może zostać zmanipulowane i pojawią się problemy. Kiedy jednak wejdziesz w "groove" robienia utf-8, wszystko stanie się drugą naturą. Oczywiście PHP6 ma być w pełni unikodową skargą od getgo, co znacznie ułatwi (mam nadzieję)

+0

Dziękuję bardzo! Ponieważ istnieje wiele poprawnie zakodowanych łańcuchów w DB, co powoduje, że Problem jest gorszy, wybrałem str_replace the Strings, które wiem, że są uszkodzone przez ich prawidłowe Postacie. Działa świetnie. Mam już zaimplementowane większość twoich porad dotyczących PHP i konfiguracji serwera, ale jest to świetne podsumowanie, więc wybrałem to jako odpowiedź, ponieważ moje rozwiązanie nie jest naprawdę piękne. –

+0

Jedna ważna uwaga na temat tej porady: NIE dodawaj 'utf-8' jako drugiego argumentu funkcji htmlspecialchars(). Bez argumentu funkcja ta działa poprawnie z ciągami UTF-8, ponieważ ignoruje wszystkie bajty z ustawionym wysokim bitem i przekazuje je. To je ochroni i "zrobi to, co trzeba". Za pomocą "utf-8", htmlspecialchars() interpretuje ciąg znaków UTF-8 - ale nie obsługuje znaków spoza BMP (o kodach U + 10000 i wyższych, zakodowanych w cztery bajty). Niepoprawnie koduje te, które pasują do modów specjalnych 65536 .. Zachowanie jest zarówno powolne, jak i błędne. – MtnViewMark

+0

Proszę zobaczyć moją odpowiedź poniżej. Rozwiązałem wszystkie te problemy w jednej funkcji czysto PHP: fixUTF8(). Nie trzeba zmieniać konfiguracji serwera, a nawet nie trzeba instalować funkcji wielobajtowych. Funkcja jest wystarczająco inteligentna, aby naprawić dowolny znak niezależnie, nawet jeśli kodowanie jest mieszane w tym samym ciągu (bez względu na to, ile razy był on konwertowany lub jest już w UTF8). –

0

Wygląda na to, że twój utf-8 jest interpretowany jako iso8859-1 lub Win-1250 w pewnym momencie.

Kiedy mówisz "W mojej bazie danych mam kilka przypadków złego kodowania" - jak to sprawdziłeś? Poprzez twoją aplikację, phpmyadmin lub klienta linii poleceń? Czy kodowanie wszystkie utf-8 wyświetla się w ten sposób lub tylko niektóre? Czy jest możliwe, że kodowanie było nieprawidłowe i zostało niepoprawnie przekonwertowane z iso8859-1 na utf-8, kiedy już było to utf-8?

+0

Używam phpmyadmin do zarządzania bazami danych. I nie, nie wszystkie przypadki są źle zakodowane. – Jayrox

2

Wiem, że to nie jest bardzo eleganckie, ale po tym, jak wspomniano, że łańcuchy mogą być podwójne zakodowany, zrobiłem tę funkcję:

function fix_double encoding($string) 
{ 
    $utf8_chars = explode(' ', 'À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö'); 
    $utf8_double_encoded = array(); 
    foreach($utf8_chars as $utf8_char) 
    { 
      $utf8_double_encoded[] = utf8_encode(utf8_encode($utf8_char)); 
    } 
    $string = str_replace($utf8_double_encoded, $utf8_chars, $string); 
    return $string; 
} 

To wydaje się działać idealnie, aby usunąć podwójne kodowanie, którego doświadczam. Prawdopodobnie brakuje mi niektórych postaci, które mogłyby być problemem dla innych. Jednak dla moich potrzeb działa idealnie.

+1

Spójrz na moją odpowiedź. Funkcja Kodowanie :: fixUTF8(). Naprawia wszystkie znaki UTF8 (są ich miliony) i obsługuje wielokrotnie zakodowane ciągi, nie tylko dwa razy. –

2

Droga jest do konwersji na binarne, a następnie do właściwego kodowania

9

Jak Dan zauważył: trzeba przekonwertować je na binarny, a następnie przekonwertować/skorygować kodowanie.

przykład dla utf8 przechowywane jako latin1 następujące SQL będzie to naprawić:

UPDATE table 
    SET field = CONVERT(CAST(field AS BINARY) USING utf8) 
WHERE $broken_field_condition 
+0

interesujące; Zapamiętam to, jeśli znowu będę miał problem. dzięki – Jayrox

+1

Ma sens. Domyślam się, że jest to podwójnie zakodowane, tylko że pole jest oznaczone flagą latin1, mimo że zawiera ono kodowanie UTF8, więc gdy zażądasz tego pola jako UTF8, koduje ono ponownie. – Eli

+0

Człowieku, zrobiłeś mój dzień, zadziałało to dla mnie. Teraz chciałbym zrozumieć prawdziwy powód, dla którego robak, z którym pracuję, ma te złe znaki (być może został poprawnie zakodowany w utf-8, ale proces zrzutu wydrukował wynik jako latin1) –

72

Jeśli utf8_encode() na sznurku, który już jest UTF-8 to wygląda na zniekształcony, gdy jest kodowany wielokrotnie.

Zrobiłem funkcję toUTF8(), która konwertuje ciągi na UTF-8.

Nie trzeba określać, jakie jest kodowanie ciągów. Może to być Latin1 (iso 8859-1), Windows-1252 lub UTF8, lub połączenie tych trzech.

Użyłem tego na kanale z mieszanym kodowaniem w tym samym ciągu.

Zastosowanie:

$utf8_string = Encoding::toUTF8($mixed_string); 

$latin1_string = Encoding::toLatin1($mixed_string); 

Moja druga funkcja fixUTF8() poprawki zniekształcone ciągi UTF8 jeśli były kodowane w UTF8 wiele razy.

wykorzystania:

$utf8_string = Encoding::fixUTF8($garbled_utf8_string); 

Przykłady:

echo Encoding::fixUTF8("Fédération Camerounaise de Football"); 
echo Encoding::fixUTF8("Fédération Camerounaise de Football"); 
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football"); 
echo Encoding::fixUTF8("Fédération Camerounaise de Football"); 

wyjście wola:

Fédération Camerounaise de Football 
Fédération Camerounaise de Football 
Fédération Camerounaise de Football 
Fédération Camerounaise de Football 

Download:

https://github.com/neitanod/forceutf8

+1

Wygląda na to, że trzeba.Nie używam go do normalnego wyświetlania, ale cieszę się, że używasz swojej klasy do pomocy w migracji danych. –

+6

Dzięki. To magiczne, prawda? Myślę, że ten mały kawałek kodu jest jedną z najbardziej satysfakcjonujących rzeczy, które stworzyłem, pod względem problemów z nim rozwiązanych. :-) –

+0

Po drugie, jest to świetny kawałek PHP – Nick

86

Jeśli masz podwójne zakodowane znaki UTF8 (różne inteligentne cytaty, myślniki, apostrofy, znak cudzysłowu, itd.), W mysql możesz zrzucić dane, a następnie odczytać je z powrotem, aby naprawić zepsute kodowanie .

Jak to:

mysqldump -h DB_HOST -u DB_USER -p DB_PASSWORD --opt --quote-names \ 
    --skip-set-charset --default-character-set=latin1 DB_NAME > DB_NAME-dump.sql 

mysql -h DB_HOST -u DB_USER -p DB_PASSWORD \ 
    --default-character-set=utf8 DB_NAME < DB_NAME-dump.sql 

To był 100% fix dla mojego podwójnego zakodowanej UTF-8.

Źródło: http://blog.hno3.org/2010/04/22/fixing-double-encoded-utf-8-data-in-mysql/

+2

Wygląda na to, że udało mi się przekonwertować bazę danych Typo3. Dzięki za zamieszczenie tego; jest znacznie czystszy niż jakakolwiek inna metoda konwersji. :) – Energiequant

+0

Żałuję, że nie mogę dać ci więcej przebojów, naprawdę na nie zasługujesz. – Frost

+0

Tak, również pracował dla mnie! Dzięki temu, że dzielisz się tym tutaj i dzięki właścicielowi bloga :) – Prine

0

miałem ten sam problem, dawno temu, i to naprawić go za pomocą

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-15"> 
0

znalazłem rozwiązanie Po wielu dniach poszukiwań. Mój komentarz zostanie pochowany, ale i tak ...

  1. Dostaję uszkodzone dane za pomocą php.

  2. I nie używaj Ustaw nazwy UTF8

  3. używam utf8_decode() na przetwarzanie moich danych

  4. zaktualizować bazę danych z moich nowych zakodowanych danych, nadal nie stosując zestaw nazwisk UTF8

voila :)

8

miałem problem z pliku xML, który miał złamane kodowania, to stwierdzono, że był UTF-8, ale miał znak aktorów, którzy nie używają utf-8.
Po kilku prób i błędów z mb_convert_encoding() udaje mi się go naprawić z

mb_convert_encoding($text, 'Windows-1252', 'UTF-8') 
+1

To zadziałało dla mnie po kilku dniach obalania mojej głowy nad tym problemem (wszystko było UTF-8 od końca do końca, ale w RSS to nie było!) Dziękuję! – Titan

+0

Mój problem: Pola bazy danych zapisane jako 'latin1_swedish_ci', wyprowadzane przez PHP jako utf-8 pokazujące Umlaute' ü' jako 'ü' i' ö' jako '¶¶'. Pomogło to to naprawić. –

1

Inną rzeczą, aby sprawdzić, co się stało z moim rozwiązaniem (znaleziono here), to w jaki sposób dane są zwracane z serwera. W mojej aplikacji używam PDO do połączenia z PHP do MySQL.Musiałem dodać flagę do podłączenia który powiedział uzyskać dane z powrotem w formacie UTF-8

Odpowiedź była

$dbHandle = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8", $dbUser, $dbPass, 
    array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'")); 
0

Skrypt ten miał ładny podejścia. Przekształcenie go w wybranym języku nie powinno być zbyt trudne:

http://plasmasturm.org/log/416/

#!/usr/bin/perl 
use strict; 
use warnings; 

use Encode qw(decode FB_QUIET); 

binmode STDIN, ':bytes'; 
binmode STDOUT, ':encoding(UTF-8)'; 

my $out; 

while (<>) { 
    $out = ''; 
    while (length) { 
    # consume input string up to the first UTF-8 decode error 
    $out .= decode("utf-8", $_, FB_QUIET); 
    # consume one character; all octets are valid Latin-1 
    $out .= decode("iso-8859-1", substr($_, 0, 1), FB_QUIET) if length; 
    } 
    print $out; 
}