Kodowanie polskich znaków

Kodowanie znaków

Kodowanie polskich znaków to zapis konkretnego znaku w postaci bajtów rozumianych przez komputer, które następnie są zamieniane na wizualne litery i cyfry widoczne dla końcowego użytkownika. Do obsługi polskiego alfabetu zaleca się kodowanie UTF-8, jest ono wielobajtowym kodowaniem które to dla wszystkich narodowych i diakrytycznych znaków stosuje zapis za pomocą dwóch bajtów za wyjątkiem systemów pisma CJK to jest wywodzących się z pisma chińskiego, kodowanie utf8 obsługuje standard Unicode dla wszystkich znaków i języków świata. Kodowaniem – dość powszechnie stosowanym – z rodziny ISO/IEC które posiada obsługę polskich znaków jest ISO-8859-2; jest ono z reguły używane na starszych stronach i wspiera tylko kilka języków oraz ma bardzo ograniczony zakres znaków, które jest w stanie wyświetlić.

Problemy z kodowaniem polskich znaków

Do najbardziej znanych problemów należą takie, w których wyświetlają nam się krzaki lub znaki zapytania zamiast polskich znaków. Spowodowane to jest zazwyczaj tym, że odczytujemy dane znaki w innym kodowaniu niż zostały one zapisane lub przez błędną konwersję, powoduje to że dany bajt będący innym znakiem w zapisanym kodowaniu w tym, w którym go odczytujemy jest zupełnie innym znakiem. Naprawa kodowania polskich znaków bazy danych MySQL powinna nam pomóc lecz tylko wtedy gdy fizycznie w pliku nie znajdują się znaki zapytania bo jeśli tak jest tu szukaj gdziekolwiek kopi, w której są polskie znaki lub krzaki. (nie chodzi tutaj o wyświetlanie znaków zapytania lecz o to czy fizycznie znajdują się w pliku; czy są bajtowo zapisane w systemie hex jako 0x3F) Poniżej prezentuję przykładowy tekst odczytany w innym kodowaniu niż został zapisany, co spowodowało wyświetlenie zupełnie innych liter niż powinny być:

Naprawa kodowania bazy danych MySQL

Przykłady zdań z polskimi znakami będące pangramami zapisane w pewnym kodowaniu a następnie odczytane jako krzaki w zupełnie innym.

Twoje pliki nie powinny posiadać BOM dla kodowania UTF-8 jeżeli chcesz usunąć tę deklarację ze wszystkich plików użyj remove BOM usuwa automatycznie ją ze wszystkich plików z katalogu, w którym zostanie uruchomiony i w podkatalogach.

Pamiętaj, że deklaracja kodowania wysłana w nagłówku HTTP stoi ponad tą zawartą w dowolnym dokumencie (np. html, CSS, javascript). Umożliwi to konfiguracja serwera Apache dla danych plików; ustawienie kodowania i typu treści.

Kodowanie znaków w edytorze tekstu

Z wyżej wymienionych przyczyn należy wywnioskować, że aby prawidłowo odczytać znaki należy ustawić kodowanie. Powinniśmy zacząć od naszego edytora tekstu; najczęściej przy otwieraniu pliku mamy okienko możliwością wybrania kodowania, można także na stałe w ustawieniach edytora ustawić odpowiednie:

Naprawianie kodowania bazy danych MySQL

Wybór kodowania znaków w edytorze tekstu

Kodowanie znaków w dokumencie html

Ustawienie kodowania nie może ograniczać się jedynie do naszego edytora tekstu z tego powodu, że najczęściej nasze dane odczytuje przeglądarka w dokumencie html i mimo tego, że zapisaliśmy nasz tekst w poprawnym kodowaniu to zostanie odczytany w domyślnym dla przeglądarki i danego systemu. Aby strona WWW przyjęła i obsłużyła poprawnie kodowanie należy w sekcji <head></head> przed tytułem <title></title> do dokumentu wstawić odpowiedni znacznik <meta> definiujący typ treści oraz kodowanie znaków w formacie "typ/treści; charset=kodowanie":

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Kodowania znaków w dokumencie XML

Deklaracja kodowania XML powinna się znaleźć w pierwszej linijce dokumentu. Deklaracja XML:

<?xml version="1.0" encoding="utf-8"?>

Kodowanie dla arkuszy stylów CSS

Deklaracja kodowania CSS nie może być poprzedzona żadnym innym znakiem – nawet deklaracją BOM – musi być ona na samym początku dokumentu.

@charset "UTF-8";

Kodowanie bazy danych MySQL

Ustawienie kodowania bazy danych to już jest bardziej skomplikowana sprawa. Ponieważ z reguły nie mamy dostępu do pliku konfiguracyjnego oprócz prywatnych serwerów, dlatego wtedy należy ustawić kodowanie i porównanie z poziomu skryptu łączącego się z bazą danych. Porównanie połączenia (collate, collation) to nic innego jak kolejność znaków jaka jest brana po uwagę przy sortowaniu wyników itp. za pomocą liter.

Aby zobaczyć listę dostępnych kodowań oraz odpowiednich dla nich domyślnych porównań na serwerze bazy danych należy wykonać zapytanie:

SHOW CHARACTER SET;
Lista kodowań obsługiwanych przez bazę danych MySQL

Wynik zapytania to lista kodowań obsługiwanych przez bazę danych MySQL

Lista dostępnych porównań dla poszczególnych kodowań zostanie zwrócona po wykonaniu zapytania:

SHOW COLLATION LIKE 'utf8%';

Gdzie utf8 to nazwa kodowania dla którego chcemy zobaczyć listę porównań.

Lista porównań dostępnych dla kodowania utf8 w bazie danych MySQL

Lista porównań dla kodowania utf8 obsługiwanych przez serwer MySQL

Poniżej prezentuję ustawienia dla poszczególnych szczebli na serwerze MySQL zgodnie z którymi dziedziczone są ustawienia kodowań tj. serwer ⇒ baza danych ⇒ tabele ⇒ pola, kolumny

Ustawienie kodowania dla serwera MySQL

Aby nasz serwer pracował w danym kodowaniu należy dodać dwa wpisy do pliku konfiguracyjnego, jedne z nich definiuje kodowanie a drugi porównanie. Wymaga to od nas dostępu do tegoż pliku co z reguły wiąże się z tym że musimy być właścicielami prywatnego serwera. Otwórz plik my.ini i w sekcji [mysqld] dodaj dwie linijki:

character-set-server=utf8
collation-server=utf8_unicode_ci

Zapisz plik i zrestartuj serwer, jeśli wszystko wykonałeś poprawnie to serwer będzie pracował w podanym kodowaniu i porównaniu. Aby sprawdzić aktualne ustawienia kodowania wykonaj zapytanie (wartość dla serwera to character_set_server):

SHOW VARIABLES LIKE 'character_set%';
Lista aktualnych ustawień serwera dotyczących kodowania znaków

Wynik zapytania pokazującego aktualne ustawienia kodowania znaków serwera MySQL

natomiast aby sprawdzić ustawienia porównań (wartość dla serwera to collation_server):

SHOW VARIABLES LIKE 'collation%';
Porównanie połączeń na serwerze MySQL

Wynik zapytania pokazującego aktualnie ustawienie porównań dla połączeń MySQL

Te ustawienia serwera będą dziedziczyć wszystkie nowo tworzone bazy danych, tabele i pola oraz kolumny chyba że przy ich tworzeniu ustawisz odpowiednio inne kodowanie lub ich rodzic będzie miał ustawione odmienne kodowanie.

Ustawienie kodowania dla bazy danych MySQL

Każda baza danych posiada własne domyślne ustawienia kodowania i porównanie połączenia dziedziczone od ustawień serwera. Kodowanie od bazy danych dziedziczą natomiast tabele. Aby ustawić kodowanie dla konkretnej bazy danych należy wykonać zapytanie:

ALTER DATABASE `nazwa_bazy` DEFAULT CHARACTER SET = 'utf8' DEFAULT COLLATE = 'utf8_unicode_ci';

Gdzie nazwa_bazy należy zamienić na właściwą nazwę bazy danych dla której chcemy ustawić kodowanie. Kodowanie dla tabel ani pól w tabelach nie zostanie zmienione.

Ustawianie kodowania dla nowo tworzonej bazy danych MySQL

Kodowanie dla bazy danych da się także ustawić już przy tworzeniu bazy danych niezależnie od ustawień serwera można to uzyskać wykonując zapytanie:

CREATE DATABASE IF NOT EXISTS `nazwa_bazy` DEFAULT CHARACTER SET = 'utf8' DEFAULT COLLATE = 'utf8_unicode_ci';
Ustawienie kodowania dla tabeli w bazie danych MySQL

Tabele w bazie posiadają własne ustawienia dotyczące porównań jak i kodowania znaków, które dziedziczą z ustawień bazy danych, w której się znajdują. Pola / kolumny dziedziczą te ustawienia od tabel. Ustawienie kodowania i porównania dla tabeli odbywa się przez zapytanie:

ALTER TABLE `nazwa_tabeli` DEFAULT CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci';

Zamiast nazwa_tabeli należy podać właściwą nazwę tabeli dla której chcemy ustawić kodowanie i porównanie. Kodowanie dla pól i kolumn pozostanie niezmienione.

Ustawianie kodowania dla nowo tworzonej tabeli w bazie danych MySQL

Kodowanie dla tabeli można zdefiniować niezależnie od ustawień serwera oraz danej bazy danych, w której ta tabela się znajduje. Można to zdefiniować przy tworzeniu tabeli, pola i kolumny będą dziedziczyć to ustawienie. Przykładowe zapytanie tworzące tabelę o nazwie nazwa_tabeli ze zdefiniowanym kodowaniem i porównaniem dla tabeli:

CREATE TABLE `nazwa_tabeli` (
`pole_tekstowe` text
) ENGINE = MYISAM DEFAULT CHARSET = 'utf8' DEFAULT COLLATE = 'utf8_unicode_ci';
Ustawienie kodowania dla pól / kolumn w tabelach

Kolumny / pola w tabelach dziedziczą ustawienia kodowania po tabelach, w których istnieją. Zmiana tego ustawienia wymaga znajomości typu tego pola oraz jego nazwy jak i nazwy tabeli. Przykładowe zapytanie zmieniające ustawienia kolumny typu varchar(30):

ALTER TABLE `nazwa_tabeli` CHANGE `nazwa_pola` `nazwa_pola` varchar(30) CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci';

nazwa_pola musi być podana dwukrotnie i zmieniona na nazwę właściwego pola, varchar(30) należy zmienić na aktualny typ pola; natomiast zanazwa_tabeli trzeba wpisać nazwę tabeli, w której istnieje to pole.

Ustawianie kodowania dla pól i kolumn przy ich tworzeniu oraz dodawaniu

Pola dziedziczą kodowanie z ustawień tabeli; można dla nich także zdefiniować kodowanie przy tworzeniu tabeli co demonstruje poniższy przykład, tworzy tabelę nazwa_tabeli w kodowaniu utf8 a w niej pole_tekstowe w kodowaniu latin2:

CREATE TABLE `nazwa_tabeli` (
`pole_tekstowe` text CHARACTER SET latin2 COLLATE latin2_general_ci
) ENGINE = MYISAM DEFAULT CHARSET = 'utf8' DEFAULT COLLATE = 'utf8_unicode_ci';

Natomiast gdy do już istniejącej tabeli chcemy dodać pole to nasze zapytanie powinno zawierać odpowiednią definicje kodowania widoczną niżej:

ALTER TABLE `nazwa_tabeli` ADD `nazwa_pola` TEXT CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci';
Kodowania dla połączenia z bazą danych MySQL w PHP

Gdy już mamy ustawioną bazę danych oraz dokument html pracuje w tym samym kodowaniu to należałoby także ustawić tak aby nasz skrypt łączący się z bazą także pracował w tym kodowaniu. Można o uzyskać poprzez wykonanie zapytania tuż po połączeniu z bazą lub wybraniu bazy danych, dzięki temu wyniki będą także zwracane w wybranym kodowaniu:

SET NAMES 'utf8' COLLATE 'utf8_unicode_ci';

to jedno zapytanie ustawia jednocześnie kilka zmiennych tj.:

SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;
SET collation_connection = y;

Gdzie x to kodowanie a y to system porównań dla niego.
Zmiany które się dokonują poprzez to zapytanie można łatwo sprawdzić dwoma zapytaniami, pierwsze dla kodowania:

SET NAMES 'macce' COLLATE 'macce_bin';
SHOW VARIABLES LIKE 'character_set%';

oraz dla porównań:

SET NAMES 'macce' COLLATE 'macce_bin';
SHOW VARIABLES LIKE 'collation%';

Przykładowy skrypt PHP, który tuż po połączeniu ustawia kodowanie:

<?php
$link = mysql_connect('127.0.0.1', 'username', 'password') or die(mysql_error());
mysql_query('SET NAMES \'utf8\' COLLATE \'utf8_unicode_ci\';', $link);
?>
Kodowanie znaków i ustawienia językowe w skryptach PHP

Kodowanie w skryptach PHP zależy także od naszego edytora tekstu, niektóre funkcje korzystają z domyślnych ustawień interpretera, które mogą nie być zgodne z naszym tekstem. Należałoby także ustawić domyślną strefę czasową. Kodowanie dla rozszerzenia mbstring jak i lokalne ustawienia języka maja wpływa na niektóre funkcje. Poniżej przedstawiam parę linijek kodu jakie zebrałem aby można było łatwo ustawić wszystko:

<?php
//Ustawienie strefy czasowej
date_default_timezone_set('Europe/Warsaw');
//Ustawienie kodowania dla bilbioteki mbstring
mb_internal_encoding('UTF-8');
//zmiana ustawień regionalnych na polski
setlocale(LC_ALL, 'pl_PL.UTF-8','pl.UTF-8','pol.UTF-8','plk.UTF-8','polish.UTF-8','poland.UTF-8');
//domyślne kodowanie dla wyjściowego dokumentu php; pozostaw puste aby wysłać tylko "Content-type: text/html" zgodnie z default_mimetype; jest to kodowanie wysyłane w nagłówku odpowiedzi HTTP
//ini_set('default_charset', 'UTF-8');
//nagłówek definiujący typ treści oraz kodowanie
header('Content-type: text/html; charset=utf-8');
?>
Kodowanie znaków na serwerze Apache

Serwer apache wysyłając pliki musi dodać do nich nagłówki, w tych nagłówkach znajduje się typ treści oraz kodowanie jeśli jest niezbędne. Nagłówek z kodowaniem *wymagany jest przy typach treści text/plain oraz text/html (*nie jest wymagany ale musisz go dodać żeby został prawidłowo odczytany). Kodowanie na serwerze można zdefiniować na kilka sposobów, ustawić domyślne dla serwera, dla danych typów plików lub poprzez wymuszenie typu bądź ustawienie nagłówka. Wszystkie ustawienia należy dodawać do pliku .htaccess na samym początku. Deklarację kodowań i typów można ustawiać dla plików stylów CSS jak i skryptów JavaScript.

#domyslne kodowanie dla serwera i plikow
AddDefaultCharset utf-8
 
#definiowanie kodowania dla plików z rozszerzeniem
AddCharset UTF-8 .htm .html .txt
 
#zmiana typu pliku o danym rozszerzeniu
AddType 'text/html; charset=UTF-8' .htm .html .txt
AddType 'text/css; charset=UTF-8' .css
AddType 'text/javascript; charset=UTF-8' .js
 
#wymuszenie typu pliku i kodowania dla danych plików; folderów etc.
<FilesMatch "\.(htm|html|txt)$">
	ForceType 'text/html; charset=UTF-8'
</FilesMatch>
<FilesMatch "\.css$">
	ForceType 'text/css; charset=UTF-8'
</FilesMatch>
<FilesMatch "\.js$">
	ForceType 'text/javascript; charset=UTF-8'
</FilesMatch>

7 Comments

  1. Użyteczny artykuł, który pomógł ustawić kodowanie w mojej aplikacji.

  2. wow, gdybym od razu tu trafił zaoszczędziłbym nerw i całego dnia.
    Kodowania dla połączenia z bazą danych MySQL w PHP rozwiązało mój problem, ale nie wiem czemu. Serwer ustawiony na latin1, to oznacza że tekst w UTF z PHP leciał najpierw przez latin1 a potem znow z latin1 na UTF do bazy?

  3. Pierwsze kompleksowe i przystępne wytłumaczenie tematu! Wielkie dzięki!
    W końcu działa.

    • tosiek

      19.04.2012 at 19:14

      Dobrze, że są jeszcze ludzie co piszą, że się przydało.

      Dobrze, że się przydało.

  4. Przydało się ;) Dzięki!

  5. Rafał Bieleniewicz

    30.08.2013 at 10:10

    Siedziałem na tym kolejny raz kolejne 3 godziny. Dzięki Twoim poradom, skrypt który napisałem (w UTF8) poprawnie zapisuje dane do MySql (utf8_unicode_ci). Być może zwykły restart bazy danych, restart mysql miał na to wpływ bo ustawiałem ją wiele razy. Czy wydanie komendy SET NAMES UTF8 automatycznie powinno zmienić kodowanie zapisanych już w bazie danych czy dopiero po restarcie bazy? Importowałem, usuwałem, zmieniałem i nic. Wykonałem wszystkie kroki które opisałeś w artykule i w końcu zobaczyłem polskie znaki zaraz po zaimportowaniu danych. DZIĘKI!!!!!!!!!!!

  6. Bałaganiarz

    30.10.2013 at 20:05

    Bardzo przydatny artykuł. Wielkie podziękowania dla autora.

Dodaj komentarz