Overstap van iso-8859-1 naar UTF-8
Nu weet ik dat ik er aan aantal randvoorwaarden zijn waaraan een site moet voldoen:
- Bestanden opslaan als UTF-8 encoding.
- Gebruik metatag met: <meta charset="UTF-8">
- Gebruik PHP-header header('Content-Type: text/html; charset=utf-8');
- Gebruik de juiste characterencoding in de DB-connectie: $mysqli->set_charset("utf8");
- Gebruik de juiste collatie: utf8_unicode_ci
Nu heb ik nog de latin1_swedish_ci collatie voor mijn site (tja, waarom Zweeds?) en dat moet dus utf8_unicode_ci worden. Is dat nog steeds het beste? En hoe converter ik de boel van de oude collatie naar de nieuwe?
Iemand goede tips & tricks, of eventuele andere dignen waar ik op moet letten, of tips wat een handig hulpmiddel kan zijn. Het is geen sinecure om deze overstap te maken, heb ik het idee, maar het moet toch eens een keer gebeuren. Ik zag ook iets over dit script:
https://github.com/nicjansma/mysql-convert-latin1-to-utf8/blob/master/mysql-convert-latin1-to-utf8.php
Volgens mij kan ik het goed gebruiken, of kleven er nog wat nadelen aan?
Dus anybody tips and tricks voor een makkelijke overstap?
Uiteraard ga ik alles natuurlijk testen!
Gewijzigd op 06/06/2018 00:43:46 door - Ariën -
- volledige export maken (mysqldump), incl "create" statements
- in een texteditor (Notepad++) de encoding naar UTF-8 zetten
- en overal "latin1_swedish_ci" vervangen door "utf8_unicode_ci" (ik zou meteen voor "utf8mb4_unicode_ci" gaan)
- oude database renamen (of verwijderen - als je niet aan "backups" doet)
- geconverteerde script inlezen
Zou dat lukken? Ikzelf gebruik in het algemeen NetBeans.
En heeft iemand ervaring met het omzetten van de bestanden op een Windows-systeem naar UTF-8?
De overstap naar utf8mb4_unicode_ci lijkt me idd een beter alternatief, omdat ik lees dat dergelijke emoji's daar beter in ondersteund worden.
En dat is dus op Windows.
Code (php)
1
mysqldump --add-drop-table database_to_correct | replace CHARSET=latin1 CHARSET=utf8 | iconv -f latin1 -t utf8 | mysql database_to_correct
Uiteraard zijn replace en iconv ook op Windows bruikbaar, en dan werkt het vast ook wel.
En ik las dat de Windows Powershell erg krachtig schijnt te zijn voor zulke taken.
Meer hoef je niet te doen (behalve wellicht collation op een soortgelijke wijze aanpassen).
Dit zou je eerst kunnen controleren door de volgende checks uit te voeren:
- connect zoals je dit nu doet, stel geen charset in als je dit voorheen niet deed, je wilt de huidige situatie van de communicatie met je database nabootsen
- vraag wat tekst op met exotische karakters, en vraag tevens de HEX() variant op van deze kolom
- lees dit spul uit in een document met ISO-8859-1 header of metatag, of hoe je dit nu doet, je wilt de huidige situatie aan de output-kant nabootsen
- lees aan de PHP-kant deze kolom uit, en strtoupper(bin2hex()) toegepast op deze kolom; als deze variant verschilt van de HEX() variant van de MySQL-kant van het verhaal houdt dit in dat de MySQL-database zich genoodzaakt voelde om onderweg vertalingen uit te voeren van character encoding A naar character encoding B; het is dan ook onduidelijk of de data ook echt latin1-encoded was en zul je eerst uit moeten zoeken wat er aan de hand is; het idee van HEX() en bin2hex() is dat deze "doof" zijn voor character encoderingen en zijn dus ook ongevoelig voor vertalingen hiertussen; op deze manier kun je dus de oorspronkelijke data uit de db op een goede manier vergelijken met hoe het uiteindelijk uitgespuugd wordt, en die twee varianten moeten gelijk zijn anders is er iets niet pluis
Misschien doe je er ook verstandig aan om in je formulieren de volgende property + waarde op te nemen:
Hier had ik in een vaag ver verleden ook al eens een artikeltje over geschreven.
Misschien is het volgende ook belangrijk voor de beeldvorming: set_charset() is in feite een contract wat jij hebt met jouw database. Hierin spreek je af dat:
- alle informatie die jij aanlevert de overeengekomen character encoding heeft
- de database zijn best doet om data aan te leveren in deze character encoding
Je kunt dus best een latin1-database hebben en set_charset('utf8') gebruiken, MySQL zal dan zijn best doen om de latin1-data te vertalen naar utf8. Dit zal in de praktijk weinig problemen opleveren omdat de "set karakters" van latin1 vele malen beperkter is dan die van utf8. Het wordt natuurlijk een wat ander verhaal als je hier vervolgens UTF-8 data in blijft stampen, dit ziet er dan mogelijk niet fris uit in latin1. Immers, de karakters die utf8 "ondersteunt" zijn mogelijk niet beschikbaar in latin1.
Het is hoogstwaarschijnlijk eenvoudiger en efficiënter (geen conversies nodig) wanneer je overal de character encoderingen gelijkschakelt.
Gotcha: mogelijk moet je de lengte van de kolommen herzien. Data die voorheen "pastte" past nu wellicht niet meer omdat karakters meer bytes kunnen innemen. Er is blijkbaar een verschil hoe MySQL -afhankelijk van de versie- karakters (of bytes) van kolomdefinities telt.
Gewijzigd op 06/06/2018 16:29:27 door Thomas van den Heuvel
set_charset('utf8') een on-the-fly encoding doet, terwijl je genoemde ALTER-query de boel daadwerkelijk converteert? Twee verschillende keuzes dus?
Enige puntje is dat je dus sowieso UTF-8 moet gebruiken als file-encoding, meta-tag en header?
En wat is nu de oorzaak/oorzaken van de volgende bekende issues?
- Dat je rare tekens ziet bij tekens met diakrieten, zoals: Ari�n -> Ariën
- Een ruitje met een vraagteken
- Een vraagteken alleen?
Gewijzigd op 06/06/2018 23:45:28 door - Ariën -
- Ariën - op 06/06/2018 23:29:43:
Wat ik begrijp is dus dat:
set_charset('utf8') een on-the-fly encoding doet, terwijl je genoemde ALTER-query de boel daadwerkelijk converteert? Twee verschillende keuzes dus?
set_charset('utf8') een on-the-fly encoding doet, terwijl je genoemde ALTER-query de boel daadwerkelijk converteert? Twee verschillende keuzes dus?
Nee, bekijk het "contract" fragment hierboven nogmaals. Hiermee geef je aan dat je middels "utf8" wilt communiceren (en dit heeft ook gevolgen voor je escaping-functionaliteit zoals real_escape_string(), daarom is het mede belangrijk dat je set_charset() gebruikt in plaats van een SET NAMES query, want in die opzet is alleen MySQL op de hoogte van de encoding die je wilt gebruiken, maar PHP niet). Jij draagt er zorg voor dat alles richting MySQL daadwerkelijk utf(-)8 is, en MySQL probeert alles in utf(-)8 terug te geven. Als dat inhoudt dat er aan de MySQL-kant conversies uitgevoerd moeten worden, dan zal deze dit automatisch onder water doen (dus ja, het kan zijn dat er on the fly dingen omgezet worden, maar dat staat niet op voorhand vast). MySQL doet eigenlijk verrassend veel achter de schermen. Als er ergens iets misgaat, houdt dat in dat de programmeur er een potje van heeft gemaakt :).
- Ariën - op 06/06/2018 23:29:43:
Enige puntje is dat je dus sowieso UTF-8 moet gebruiken als file-encoding, meta-tag en header?
Alles expliciet instellen is altijd beter dan hopen dat de defaults kloppen. En ja, het is belangrijk dat je aangeeft hoe data er uit zou moeten zien. Vergelijk dit met een spreektaal. Als je engelse teksten hebt, en je zet hierboven "dit is frans"... dan kan een uitsluitend franssprekend publiek daar geen chocola van maken.
- Ariën - op 06/06/2018 23:29:43:
En wat is nu de oorzaak/oorzaken van de volgende bekende issues?
- Dat je rare tekens ziet bij tekens met diakrieten, zoals: Ari�n -> Ariën
- Dat je rare tekens ziet bij tekens met diakrieten, zoals: Ari�n -> Ariën
Meerdere oorzaken mogelijk, bijvoorbeeld:
- dubbele encoding bij opslaan van de data. Stel je had oorspronkelijk geen CE ingesteld bij het maken van een connectie. Vaak is deze dan latin1. Maar je tabellen zijn utf8. Dan is dit voor MySQL aanleiding om te utf8-encoden bij opslag. En vaak is de aangeleverde data toch al utf8 (resultaat: dubbel geëncodeerde data). Dan wordt er iemand op een gegeven moment wakker en fixt de set_charset() aanroep (of stelt deze fatsoenlijk in). Dan is er voor MySQL geen aanleiding meer om iets (terug) te converteren omdat de tabel ook utf8 is, en de "gesproken taal" staat al ingesteld op "utf8". Dit in tegenstelling zoals bij de impliciete latin1-instelling het geval was, deze doet dan een dappere poging om utf8 terug te converteren naar latin1 waarbij in feite de dubbele encoding ongedaan wordt gemaakt en het lijkt er dan op dat alles goed gaat, maar dat komt dus pas naar buiten wanneer je set_charset() repareert, en dan heb je dus een heleboel
- geen character encoding instelling in het document waarin de data wordt weergegeven, zoals de vacature-banner en de nieuwsticker op deze site, waar ik ik weet niet hoe vaak al op gewezen heb...
- data is niet van de voorgeschreven CE
- Ariën - op 06/06/2018 23:29:43:
- Een ruitje met een vraagteken
Dat is vaak een verkeerd geëncodeerd teken. Of een multibyte karakter wat niet pastte in de utf8-variant ofzo. Het is wel een kleine prestatie als je dit voor elkaar krijgt. En aanleiding om je enigszins zorgen te maken en als de s*demieter uit te zoeken hoe dat kon :p.
- Ariën - op 06/06/2018 23:29:43:
- Een vraagteken alleen?
Dat is meestal een karakter wat niet terugvertaald kon worden, dus een niet-ondersteund teken. Vaak een exotisch karakter (uit UTF-8) dat niet ondersteund wordt door een subset (zoals ISO-8859-1).
Gewijzigd op 07/06/2018 14:47:27 door Thomas van den Heuvel
Maar ik neem aan dat ik dan beter set_charset('utf8mb4') moet gebruiken, of is utf8mb4 meer een dingetje voor de collation van MySQL/MariaDB zelf, en dat PHP zich prima uit de voeten kan met UTF-8?
Thomas van den Heuvel op 07/06/2018 14:27:01:
Als er ergens iets misgaat, houdt dat in dat de programmeur er een potje van heeft gemaakt :).
Dat wil ik natuurlijk niet ;-) Dus ga ik natuurlijk voor een conversie van alle data in de database, plus de nodige verwijzingen naar UTF-8 op de genoemde plekken.
Rest mij enkel nu eventjes de vragen:
- Wat is de ervaring en mening over het in de startpost aangehaald conversie-script op GitHub? Een geautomatiseerde conversie met behoud van de FULLTEXT velden is nog niet direct mogelijk zie ik.
- Iemand goede ervaringen met het omzetten van de character encoding van de bestanden zelf in batch? Windows PowerShell to the rescue? Of is er iets wat NetBeans ook in een handomdraai kan doen?
Gewijzigd op 08/06/2018 17:25:49 door - Ariën -
- Ariën - op 08/06/2018 17:23:22:
Maar ik neem aan dat ik dan beter set_charset('utf8mb4') moet gebruiken
Je bedoelt wellicht dat je alle tabellen converteert naar utf8mb4, en dan een verbinding maakt m.b.v. set_charset('utf8mb4');? In dat geval: ja.
- Ariën - op 08/06/2018 17:23:22:
of is utf8mb4 meer een dingetje voor de collation van MySQL/MariaDB zelf, en dat PHP zich prima uit de voeten kan met UTF-8?
Collation is niet hetzelfde als character encoding (interne link). Je zult dus ook een collation moeten instellen als je de tabellen converteert (bijvoorbeeld utf8mb4_general_ci) of in ieder geval moeten controleren of deze klopt / automatisch meeverandert. PHP kent maar één variant van UTF-8 (althans de functies die CE-aware zijn :p). MySQL kent meerdere varianten. De vraag is trouwens of je utf8mb4 echt nodig hebt, je zou ook eerst een conversie naar utf8 kunnen doen.
- Ariën - op 08/06/2018 17:23:22:
Rest mij enkel nu eventjes de vragen:
- Wat is de ervaring en mening over het in de startpost aangehaald conversie-script op GitHub? Een geautomatiseerde conversie met behoud van de FULLTEXT velden is nog niet direct mogelijk zie ik.
- Wat is de ervaring en mening over het in de startpost aangehaald conversie-script op GitHub? Een geautomatiseerde conversie met behoud van de FULLTEXT velden is nog niet direct mogelijk zie ik.
Als ik het begeleidend schrijven er op nasla is dat script bedoeld om incorrecte latin1 data (wss utf8) om te zetten naar kloppende data in een utf8 tabel. Dat is min of meer exact hetzelfde wat ik deed in het tweede deel van mijn eerder gelinkte artikel. Echter is daar in jouw geval geen sprake van? Je hebt toch geen niet-kloppende latin1 data? Volgens mij is het dan nog steeds enkel een kwestie van het simpele ALTER TABLE statement uitvoeren wat eerder in dit draadje stond, misschien met toevoeging van een collation.
- Ariën - op 08/06/2018 17:23:22:
- Iemand goede ervaringen met het omzetten van de character encoding van de bestanden zelf in batch? Windows PowerShell to the rescue? Of is er iets wat NetBeans ook in een handomdraai kan doen?
Over hoeveel databases/tabellen/data gaat het? Meestal is het verstandig om eerst een kleine analyse te maken van de tabellen en de data. Waarbij eigenlijk het belangrijkste is dat je vaststelt dat de initiële data de goede encoding heeft. In mijn artikel (en hierboven) staat (weliswaar in een notedop) beschreven hoe je dit vaststelt.
Maak ook altijd eerst een backup voordat je begint. En zorg ervoor dat tijdens dit conversieproces niemand data kan toevoegen of kan wijzigen.
Gewijzigd op 08/06/2018 19:50:39 door Thomas van den Heuvel
Thomas van den Heuvel op 08/06/2018 19:48:42:
Je hebt toch geen niet-kloppende latin1 data? Volgens mij is het dan nog steeds enkel een kwestie van het simpele ALTER TABLE statement uitvoeren wat eerder in dit draadje stond, misschien met toevoeging van een collation.
Dat ga ik niet voor 100% van uit. Ik neem aan dat alles nu netjes latin-1 is, maar met data van ruim 10 jaar oud, en de vroegere tijd dat character-encoding niet interessant genoeg was kan je niks uitsluiten. Wel weet ik dat ik geen vreemde 'encoding-probleem'-tekens gezien heb. Dus ik ga er van uit dat het netjes altijd latin-1 is geweest.
Quote:
Over hoeveel databases/tabellen/data gaat het? Meestal is het verstandig om eerst een kleine analyse te maken van de tabellen en de data. Waarbij eigenlijk het belangrijkste is dat je vaststelt dat de initiële data de goede encoding heeft. In mijn artikel (en hierboven) staat (weliswaar in een notedop) beschreven hoe je dit vaststelt.
Maak ook altijd eerst een backup voordat je begint. En zorg ervoor dat tijdens dit conversieproces niemand data kan toevoegen of kan wijzigen.
Maak ook altijd eerst een backup voordat je begint. En zorg ervoor dat tijdens dit conversieproces niemand data kan toevoegen of kan wijzigen.
Stuk of 20 tabellen en 50 MB.
Uiteraard maak ik een backup en test ik het op een geisoleerde locatie uit op dezelfde server waar de site ook in productie draait.
Eerst maar uittesten dan, en dan kijken waar de bottlenecks zitten.
Gewijzigd op 08/06/2018 20:38:05 door - Ariën -