Mam aplikację, która obsługuje klientów z całego świata i oczywiście chcę, aby wszystko, co trafia do moich baz danych, było zakodowane w UTF-8.
Głównym problemem dla mnie jest to, że nie wiem, jakie kodowanie będzie miało źródło dowolnego ciągu - może to być z pola tekstowego (użycie <form accept-charset="utf-8">
jest przydatne tylko wtedy, gdy użytkownik faktycznie przesłał formularz) lub może to być z przesłanego pliku tekstowego, więc naprawdę nie mam kontroli nad danymi wejściowymi.
To, czego potrzebuję, to funkcja lub klasa, która upewnia się, że zawartość mojej bazy danych jest w miarę możliwości zakodowana w UTF-8. Próbowałem, iconv(mb_detect_encoding($text), "UTF-8", $text);
ale to ma problemy (jeśli dane wejściowe to „narzeczona”, zwraca „narzeczona”). Próbowałem wielu rzeczy = /
W przypadku przesyłania plików podoba mi się pomysł poproszenia użytkownika końcowego o określenie używanego przez niego kodowania i pokazanie podglądu tego, jak będzie wyglądać wynik, ale to nie pomaga w walce z paskudnymi hakerami (w rzeczywistości może to zmienić ich życie trochę łatwiej).
Przeczytałem inne pytania SO na ten temat, ale wydaje się, że wszystkie mają subtelne różnice, takie jak „Muszę przeanalizować kanały RSS” lub „Pobieram dane ze stron internetowych” (lub w rzeczywistości „Nie możesz”).
Ale musi być coś, co przynajmniej warto spróbować !
źródło
UTF-8//IGNORE
jako drugiego parametru wiconv
?Odpowiedzi:
To, o co prosisz, jest niezwykle trudne. Jeśli to możliwe, najlepiej jest nakłonić użytkownika do określenia kodowania. Zapobieganie atakowi nie powinno być w ten sposób dużo łatwiejsze ani trudniejsze.
Możesz jednak spróbować zrobić to:
Ustawienie wartości ścisłej może pomóc uzyskać lepszy wynik.
źródło
mb_detect_encoding
kod źródłowy w swojej dystrybucji php (gdzieś tutaj: ext / mbstring / libmbfl / mbfl / mbfl_ident.c). Ta funkcja w ogóle nie działa prawidłowo. W przypadku niektórych kodowań ma nawet „return true”, lol. Inne są w funkcjach Ctrl + c Ctrl + v. Dzieje się tak, ponieważ nie możesz wykryć kodowania bez jakiegoś słownika lub podejścia statystycznego (takiego jak moje).mb_detect_encoding
przechodzi przez listę dostarczonych kodowań i akceptuje pierwszy, który nie ma nieprawidłowych sekwencji bajtów w ciągu ... W przypadku kodowań, które nie mają nieprawidłowych sekwencji bajtów, takich jak ISO-8859-1, zawsze jest to prawda . Brak „inteligentnej” heurystyki, a wyniki różnią się znacznie w zależności od listy (i kolejności) przekazywanych kodowań.mb_detect_order()
mimo że jest to domyślna wartość tego parametru, ponieważ chciał ustawić ścisłe wykrywanie kodowania na true (trzeci parametr) :)W ojczyźnie Rosji mamy 4 popularne kodowania, więc twoje pytanie jest tutaj bardzo pożądane.
Tylko za pomocą kodów znaków symboli nie można wykryć kodowania, ponieważ strony kodowe się przecinają. Niektóre strony kodowe w różnych językach mają nawet pełne przecięcie. Tak, potrzebujemy innego podejścia .
Jedynym sposobem pracy z nieznanymi kodowaniami jest praca z prawdopodobieństwami. Nie chcemy więc odpowiadać na pytanie „co to jest kodowanie tego tekstu?”, Staramy się zrozumieć „ jakie jest najprawdopodobniej kodowanie tego tekstu? ”.
Jeden facet z popularnego rosyjskiego bloga technicznego wymyślił takie podejście:
Zbuduj zakres prawdopodobieństwa kodów znaków w każdym kodowaniu, które chcesz obsługiwać. Możesz go zbudować, używając dużych tekstów w swoim języku (np. Trochę fikcji, użyj Szekspira dla angielskiego i Tołstoja dla rosyjskiego, lol). Otrzymasz coś takiego:
Kolejny. Bierzesz tekst w nieznanym kodowaniu i dla każdego kodowania w swoim „słowniku prawdopodobieństwa” szukasz częstotliwości każdego symbolu w nieznanym zakodowanym tekście. Sumowanie prawdopodobieństw symboli. Prawdopodobnie wygrywa kodowanie z wyższą oceną. Lepsze wyniki dla większych tekstów.
Jeśli jesteś zainteresowany , chętnie pomogę Ci w tym zadaniu. Możemy znacznie zwiększyć dokładność, budując listę prawdopodobieństwa z dwoma znakami.
Przy okazji. mb_detect_encoding certanly nie działa. Tak, w ogóle. Proszę spojrzeć na kod źródłowy mb_detect_encoding w "ext / mbstring / libmbfl / mbfl / mbfl_ident.c".
źródło
Prawdopodobnie próbowałeś tego, ale dlaczego nie użyć po prostu funkcji mb_convert_encoding? Spróbuje automatycznie wykryć zestaw znaków podanego tekstu lub możesz przekazać mu listę.
Próbowałem też uruchomić:
a wyniki są takie same dla obu. Jak widzisz, że Twój tekst jest skracany do słowa „narzeczony”? czy to w bazie danych czy w przeglądarce?
źródło
iconv
. Próbowałem zrobić prawie czysty sposób mb_ *. Co o tym myśliszNie ma sposobu na zidentyfikowanie zestawu znaków łańcucha, który jest całkowicie dokładny. Istnieją sposoby, aby spróbować odgadnąć zestaw znaków. Jednym z tych sposobów, prawdopodobnie / obecnie najlepszym w PHP, jest mb_detect_encoding (). Spowoduje to przeskanowanie łańcucha i wyszukanie wystąpień elementów unikalnych dla określonych zestawów znaków. W zależności od twojego ciągu, może nie być takich rozróżnialnych wystąpień.
Weź zestaw znaków ISO-8859-1 w porównaniu z ISO-8859-15 ( http://en.wikipedia.org/wiki/ISO/IEC_8859-15#Changes_from_ISO-8859-1 )
Jest tylko kilka różnych znaków, a co gorsza, są one reprezentowane przez te same bajty. Nie ma sposobu, aby wykryć otrzymanie łańcucha bez znajomości jego kodowania, czy bajt 0xA4 ma oznaczać ¤, czy € w twoim ciągu, więc nie ma sposobu, aby poznać dokładny zestaw znaków.
(Uwaga: możesz dodać czynnik ludzki lub jeszcze bardziej zaawansowaną technikę skanowania (np. To, co sugeruje Oroboros102), aby spróbować ustalić na podstawie otaczającego kontekstu, czy postać powinna być ¤ czy €, chociaż wydaje się to być pomostem za daleko)
Jest więcej dostrzegalnych różnic między np. UTF-8 i ISO-8859-1, więc nadal warto spróbować to rozgryźć, gdy nie jesteś pewien, chociaż możesz i nigdy nie powinieneś polegać na tym, że jest poprawny.
Ciekawa lektura: http://kore-nordmann.de/blog/php_charset_encoding_FAQ.html#how-do-i-determine-the-charset-encoding-of-a-string
Istnieją jednak inne sposoby na zapewnienie prawidłowego zestawu znaków. Jeśli chodzi o formularze, staraj się egzekwować stosowanie UTF-8 tak bardzo, jak to możliwe (sprawdź bałwana, aby upewnić się, że przesyłanie będzie w formacie UTF-8 w każdej przeglądarce: http://intertwingly.net/blog/2010/07/29/Rails-and -Snowmen ) Po wykonaniu tej czynności przynajmniej możesz być pewien, że każdy tekst przesłany za pośrednictwem formularzy to utf_8. Jeśli chodzi o przesłane pliki, spróbuj uruchomić na nim polecenie unix 'file -i' poprzez np. Exec () (jeśli to możliwe na twoim serwerze), aby pomóc w wykryciu (używając BOM dokumentu). Jeśli chodzi o pobieranie danych, możesz odczytać nagłówki HTTP, które zwykle określają zestaw znaków. Podczas analizowania plików XML sprawdź, czy metadane XML zawierają definicję zestawu znaków.
Zamiast próbować automagicznie odgadnąć zestaw znaków, powinieneś najpierw spróbować samemu zapewnić określony zestaw znaków, jeśli to możliwe, lub spróbować pobrać definicję ze źródła, z którego ją otrzymujesz (jeśli ma to zastosowanie), zanim uciekniesz się do wykrywania.
źródło
Jest tutaj kilka naprawdę dobrych odpowiedzi i prób odpowiedzi na twoje pytanie. Nie jestem mistrzem kodowania, ale rozumiem Twoje pragnienie posiadania czystego stosu UTF-8 aż do bazy danych. Używam
utf8mb4
kodowania MySQL dla tabel, pól i połączeń.Moja sytuacja sprowadzała się do stwierdzenia: „Chcę tylko, aby moje środki dezynfekujące, walidatory, logika biznesowa i przygotowane oświadczenia radziły sobie z UTF-8, gdy dane pochodzą z formularzy HTML lub e-mailowych linków rejestracyjnych”. Tak więc, na swój prosty sposób, zacząłem od tego pomysłu:
$encodings = ['UTF-8', 'ISO-8859-1', 'ASCII'];
throw new RuntimeException
UTF-8
, kontynuuj.W przeciwnym razie, jeśli jest
ISO-8859-1
lubASCII
za. Próba konwersji na UTF-8 (czekaj, nie zakończono)
b. Wykryj kodowanie przekonwertowanej wartości
do. Jeśli raportowane kodowanie i przekonwertowana wartość są takie same
UTF-8
, kontynuuj.re. Jeszcze,
throw new RuntimeException
Z mojej klasy abstrakcyjnej
Sanitizer
Można by argumentować, że powinienem oddzielić zagadnienia związane z kodowaniem od mojej
Sanitizer
klasy abstrakcyjnej i po prostu wstrzyknąćEncoder
obiekt do konkretnej instancji podrzędnejSanitizer
. Jednak głównym problemem związanym z moim podejściem jest to, że bez większej wiedzy po prostu odrzucam typy kodowania, których nie chcę (i polegam na funkcjach PHP mb_ *). Bez dalszych badań nie mogę wiedzieć, czy to boli niektóre populacje, czy nie (lub, jeśli tracę ważne informacje). Muszę się więc dowiedzieć więcej. Znalazłem ten artykuł.To, co każdy programista absolutnie, pozytywnie musi wiedzieć o kodowaniu i zestawach znaków do pracy z tekstem
Co więcej, co się stanie, gdy zaszyfrowane dane zostaną dodane do moich łączy rejestracyjnych e-mail (przy użyciu
OpenSSL
lubmcrypt
)? Czy może to przeszkadzać w dekodowaniu? A co z Windows-1252? A co z konsekwencjami dla bezpieczeństwa? Użycieutf8_decode()
iutf8_encode()
wSanitizer::isUTF8
jest wątpliwe.Ludzie wskazywali na niedociągnięcia w funkcjach PHP mb_ *. Nigdy nie poświęcałem czasu na badanie
iconv
, ale jeśli działa lepiej niż funkcje mb_ *, daj mi znać.źródło
Nie sądzę, żeby to był problem. Aplikacja zna źródło danych wejściowych. Jeśli pochodzi z formularza, użyj w swoim przypadku kodowania UTF-8. To działa. Po prostu sprawdź, czy podane dane są poprawnie zakodowane (walidacja). Należy pamiętać, że nie wszystkie bazy danych obsługują UTF-8 w jego pełnym zakresie.
Jeśli jest to plik, nie zapiszesz go zakodowanego w formacie UTF-8 w bazie danych, ale w formie binarnej. Kiedy ponownie wyprowadzasz plik, użyj również wyjścia binarnego, wtedy jest to całkowicie przezroczyste.
Twój pomysł jest fajny, że użytkownik może powiedzieć kodowanie, czy i tak może to stwierdzić po pobraniu pliku, ponieważ jest on binarny.
Muszę więc przyznać, że nie widzę konkretnego problemu, który poruszysz w swoim pytaniu. Ale może możesz dodać więcej szczegółów na temat twojego problemu.
źródło
Możesz skonfigurować zestaw wskaźników, aby spróbować odgadnąć, które kodowanie jest używane. Ponownie, nie jest doskonały, ale może wyłapać niektóre błędy z mb_detect_encoding ().
źródło
mb_detect_encoding()
pudłach, czy myślisz, że moja odpowiedź ma szansę na śnieżkę latem na Saharze?Jeśli chcesz „zabrać to na konsolę”, polecam
enca
. W przeciwieństwie do raczej uproszczonegomb_detect_encoding
, używa „mieszanki analizowania, analizy statystycznej, zgadywania i czarnej magii w celu określenia ich kodowania” (lol - patrz strona podręcznika ). Jednak zwykle musisz przekazać język pliku wejściowego, jeśli chcesz wykryć takie kodowania specyficzne dla kraju. (Jednakmb_detect_encoding
zasadniczo ma te same wymagania, ponieważ kodowanie musiałoby pojawić się „we właściwym miejscu” na liście przekazywanych kodowań, aby w ogóle było wykrywalne).enca
pojawił się również tutaj: Jak znaleźć kodowanie pliku w systemie Unix za pomocą skryptówźródło
Wygląda na to, że odpowiedź na Twoje pytanie jest dość wyczerpująca, ale mam podejście, które może uprościć sprawę:
Miałem podobny problem, próbując zwrócić dane ciągów z mysql, nawet konfigurując zarówno bazę danych, jak i php, aby zwracały ciągi sformatowane do utf-8. Jedynym sposobem, w jaki otrzymałem błąd, było zwrócenie ich z bazy danych.
Wreszcie, żeglując po Internecie, znalazłem naprawdę łatwy sposób, aby sobie z tym poradzić:
Biorąc pod uwagę, że możesz zapisywać wszystkie te typy danych ciągów w swoim mysql w różnych formatach i zestawieniach, wystarczy, że w pliku połączenia php ustaw sortowanie na utf-8, na przykład:
Oznacza to, że najpierw zapisujesz dane w dowolnym formacie lub sortowaniu i konwertujesz je dopiero po powrocie do pliku php.
Mam nadzieję, że to było pomocne!
źródło
Jeśli tekst jest pobierany z bazy danych mysql, możesz spróbować dodać go po połączeniu BD.
mysqli_set_charset ($ con, "utf8");
https://www.php.net/manual/en/mysqli.set-charset.php
źródło
cURL domyślne opcje:
Próbowałem czegoś takiego. Pomogło mi. Jeśli zostanie znaleziony w informacjach o meta charset, konwertuję, w przeciwnym razie nic nie robię.
źródło