UTF en vreemde tekens

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Nicos Vermeulen

Nicos Vermeulen

01/08/2020 20:40:02
Quote Anchor link
Ik heb even wat hulp nodig want kom er niet uit. Heb inmiddels veel hierover gelezen, maar dat helpt me niet.

Heb een tabel aangemaakt in mySQL:
- Collatie: utf8mb4_unicode_ci
- 2 velden met de collatie utf8mb4_unicode_ci

In een formulier heb ik een bootstrap 3 tekstveld en een instantie van CKeditor 4. Doormiddel van een Ajax call zorg ik dat de data wordt opgeslagen in de database:

$.ajax({
url:'pages/insert.php',
method:'POST',
data:{

Bovenaan de html pagina staat:

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

Wanneer ik het woord ëèn via het tekstveld en eveneens via CKeditor op sla in de database zie ik met PHPmyAdmin het woord als volgt terug in de database:

Tekstveld: ëèn
CKeditor: &euml;&egrave;n

Wanneer ik de data weer open in het formulier met een select opdracht zie ik in de CKeditor wel weer gewoon ëèn staan, maar in het tekstveld zie ik net als rechtstreeks in de database staan ëèn.

1) Hoe zou ik dit in de database moeten zien staan? Als de collatie van zowel de tabel als het veld op utf8mb4_unicode_ci staat zou ik denken gewoon ëèn.
2) Hoe kan het dat het resultaat van het tekstveld en de CKeditor verschillend is.

Mogelijk kan iemand me op weg helpen?
 
PHP hulp

PHP hulp

08/11/2024 20:09:53
 
Thomas van den Heuvel

Thomas van den Heuvel

01/08/2020 22:51:24
Quote Anchor link
> wat zijn character encoderingen, en verder in die thread
> collations en character encoderingen zijn twee verschillende dingen

Mogelijk zijn al je problemen al opgelost indien je direct na het maken van een connectie een character encoding instelt met behulp van set_charset().

Indien je dit repareert heeft dit mogelijk wel implicaties voor data die reeds in je database zit, die is dan mogelijk dubbel geëncodeerd.

1) Hoe zou ik dit in de database moeten zien staan? Als de collatie van zowel de tabel als het veld op utf8mb4_unicode_ci staat zou ik denken gewoon ëèn.
Zoals aangegeven, collatie is iets anders dan character encoding. Indien je phpMyAdmin gebruikt om deze data te bekijken dan zou dit er gewoon goed uit moeten zien, mist dit op de goede manier is ingevoerd en de juiste character encoding staat ingesteld.

2) Hoe kan het dat het resultaat van het tekstveld en de CKeditor verschillend is.
Je connect op twee verschillende plaatsen op twee verschillende manieren met je database. Een manier (phpMyAdmin) is waarschijnlijk goed, en de ander (je eigen code/applicatie) waarschijnlijk niet.

Maar als je dit dus repareert dan ziet nieuw geinserte data er waarschijnlijk overal goed uit, maar eerder geinserte data mogelijk niet.
Gewijzigd op 02/08/2020 00:19:37 door Thomas van den Heuvel
 
Nicos Vermeulen

Nicos Vermeulen

02/08/2020 02:08:06
Quote Anchor link
Thomas van den Heuvel op 01/08/2020 22:51:24:
Mogelijk zijn al je problemen al opgelost indien je direct na het maken van een connectie een character encoding instelt met behulp van set_charset().

Helaas, dit helpt niet. Heb bij de connectiestring het volgende toegevoegd:

$conn = mysqli_connect($host,$user,$pswd,$db);
mysqli_set_charset($conn,"utf8");

Nog even een extra check uitgevoerd:
echo "Initial character set is: " . mysqli_character_set_name($conn);

Nieuw record met deze charset toegevoegd, maar krijg nog steeds te zien: ëèn

Toch raar dat de tekst die ik in CKeditor invoer wel op de juiste manier wordt opgeslagen.
Gewijzigd op 02/08/2020 02:31:54 door Nicos Vermeulen
 
Thomas van den Heuvel

Thomas van den Heuvel

02/08/2020 15:16:17
Quote Anchor link
Nicos Vermeulen op 02/08/2020 02:08:06:
Nieuw record met deze charset toegevoegd, maar krijg nog steeds te zien: ëèn


Waar krijg je dit te zien, hoe geeft je dit weer, en hoe ziet de rest van dat document er uit? Zitten hier ook meta-tags in? En/of PHP-headers?

Je kunt een hele simpele test uitvoeren om te zien of de tekst "één" juist geëncodeerd is opgeslagen. Voer op deze kolom de HEX()-functie uit in MySQL. Hier zou C3A9C3A96E uit moeten komen. Vervolgens zou je ook aan de PHP-kant de hexadecimale waarde (die verder onafhankelijk is van de gebruikte character encoding) kunnen controleren met behulp van bin2hex(), eventueel in combinatie met strtoupper() zodat je rechtstreeks een vergelijking met de waarde van HEX() kunt doen. Deze zouden beide hetzelfde moeten zijn, anders zijn er vertalingen uitgevoerd.

Overigens, je gebruikte volgens mij utf8mb4 tabellen? Dan zou dat ook in set_charset() moeten staan. utf8 (in mysql) bestrijkt een andere karakter-bereik dan utf8mb4.

edit: uitgaande van je collatie. Voer anders eens een SHOW CREATE TABLE uit op de bewuste tabel, wat voor character encoding wordt daar weergegeven? Mogelijk wijkt de definitie van je tabel af.

In wezen, of liever gezegd, idealiter, zouden alle encoderingen in de pas moeten lopen:
- de definities van de tabellen en kolommen
- de data in deze kolommen zelf
- de set_charset() direct na het maken van de connectie
- headers of meta-tags in het document (dit zou UTF-8 moeten zijn)

En collation boeit eigenlijk niet zoveel op dit moment.
Gewijzigd op 02/08/2020 17:51:18 door Thomas van den Heuvel
 
Nicos Vermeulen

Nicos Vermeulen

02/08/2020 22:06:23
Quote Anchor link
Dank je wel Thomas voor je reactie. Ik ga dit uitproberen. Ik ken de HEX functie niet, was al even op zoek, maar lukt niet om deze uit te voeren in PHPmyAdmin.

SELECT HEX(colname); Wanneer ik hier de kolomnaam opgeef krijg ik een foutmelding. Kan ik hier ook WHERE toepassen?

Gebruik inderdaad utf8mb4. De pagina die ik uitvoer om het resultaat te bekijken is een combinatie van HTML en PHP. Helemaal bovenin heb ik de meta-tag staan:

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

Ik dacht dat het voldeed als ik als meta-tag UTF-8 aangaf, ook wanneer ik als collatie utf8mb4 gebruik. Is het beter op tabel en kolom niveau een andere collatie dan utf8mb4 gebruiken?
 
- Ariën  -
Beheerder

- Ariën -

02/08/2020 22:27:01
Quote Anchor link
Een WHERE lijkt mij wel zo prettig bij een SELECT HEX(....)
 
Thomas van den Heuvel

Thomas van den Heuvel

02/08/2020 22:30:44
Quote Anchor link
> SELECT HEX(colname); Wanneer ik hier de kolomnaam opgeef krijg ik een foutmelding. Kan ik hier ook WHERE toepassen?
Uiteraard, SELECT HEX(<kolomnaam>) FROM <tabelnaam> zou moeten werken.
En als je een specifiek record zoekt met een id dan kun je hier een WHERE-conditie aan hangen.

> Ik dacht dat het voldeed als ik als meta-tag UTF-8 aangaf, ook wanneer ik als collatie utf8mb4 gebruik
Nee, je moet nog steeds set_charset() gebruiken, anders gebruikt MySQL wellicht standaard latin1, en dan gaan er dingen mis. Het hoe en het waarom staat hier (interne link) wellicht nog wat beter uitgelegd. Het komt erop neer dat je zelf verantwoordelijkheid draagt om data op de goede manier aan te leveren, en hierbij is het gewoon het handigste dat je zelf expliciet een character encoding instelt, en niet uitgaat van een (mogelijk afwijkende) default. Dat zou namelijk later, als andere applicaties data uit deze database gebruiken en wel op de goede manier een connectie maken, voor problemen kunnen zorgen.

> Is het beter op tabel en kolom niveau een andere collatie dan utf8mb4 gebruiken?
utf8mb4 is een character encoding, utf8mb4_general_ci is een collation. Dit zijn twee compleet verschillende dingen.

Collation heeft een ander doel dan character encoding, zoals in een bovenstaande link al staat uitgelegd. Tenzij je in deze kolommen zoekt en/of teksten vergelijkt, of dingen sorteert maakt de collation echt niet zoveel uit.

Daarbij kan het best zo zijn dat op database- of tabel-niveau de default collation al utf8mb4_general_ci is, en deze wordt ook geïmpliceerd door de character encoding, want elke encoding heeft een default collation, wat je als volgt kunt zien:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
SHOW CHARACTER SET LIKE 'utf%';

Dit levert iets soortgelijks als:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
+---------+------------------+--------------------+--------+
| Charset | Description      | Default collation  | Maxlen |
+---------+------------------+--------------------+--------+
| utf8    | UTF-8 Unicode    | utf8_general_ci    |      3 |
| utf8mb4 | UTF-8 Unicode    | utf8mb4_general_ci |      4 |
| utf16   | UTF-16 Unicode   | utf16_general_ci   |      4 |
| utf16le | UTF-16LE Unicode | utf16le_general_ci |      4 |
| utf32   | UTF-32 Unicode   | utf32_general_ci   |      4 |
+---------+------------------+--------------------+--------+

Dus wat dat betreft hoef je die niet expliciet in te stellen tenzij deze, wellicht om een speciale reden, afwijkt van de default.
Gewijzigd op 02/08/2020 22:33:28 door Thomas van den Heuvel
 
Nicos Vermeulen

Nicos Vermeulen

03/08/2020 20:13:30
Quote Anchor link
Dag Thomas,

Nog even bezig geweest, en het woord één opgeslagen.

Met connectie string mysqli_set_charset($conn,"utf8") krijg ik het volgende te zien op een html/php pagina: één

Via PHPmyAdmin krijg ik de volgende HEX code terug: C3A9C3A96E2074657374. Dat klopt dus, hij is dus juist geëncodeerd opgeslagen.

Nu komt het: wanneer ik mysqli_set_charset($conn,"utf8") niet meer toe pas. Zie ik op de html/php pagina netjes: één staan.

Ik snap er niets meer van...
Gewijzigd op 03/08/2020 20:38:23 door Nicos Vermeulen
 
Thomas van den Heuvel

Thomas van den Heuvel

03/08/2020 23:06:20
Quote Anchor link
Maar je tabel is utf8mb4, gebruik dan ook utf8mb4 als encoding, en niet utf8. utf8 is ook niet hetzelfde als UTF-8.

Probeer nadat je dit hebt aangepast de tekst eens opnieuw in te voeren. En dan niet via phpMyAdmin, maar via jouw applicatie, en controleer dan de HEX()-waarde opnieuw.

Kijk ook eens aan de clientzijde (PHP) wat er daar van gemaakt wordt m.b.v. strtoupper(hex2bin($row['column']));.

Dit zou hetzelfde moeten zijn als aan de serverzijde.

Wat voor webserver/PHP-versie gebruik je trouwens? Mogelijk voegt je webserver extra headers toe? Kijk eens in je netwerk-tab wat de character encoding van de response is? Anders stel deze expliciet in met behulp van:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
header('Content-Type: text/html; charset=UTF-8');
?>

Deze header-aanroep zorgt er tevens voor dat enige andere ingestelde Content-Type header overschreven wordt.

NB het feit dat je één als output krijgt wil zeggen dat deze tekst op een of andere manier nogmaals als UTF-8 geëncodeerd wordt.

Dus:
1. zorg dat alle character encoderingen kloppen en gelijk* lopen;
2. voer dan informatie in via je applicatie;
3. controleer of het resultaat klopt.

Misschien helpt het ook dat je in je formulieren een accept-charset attribuut met als waarde UTF-8 toevoegt.

* gelijk lopen wil zeggen:
- een UTF-8 header middels een PHP-header
- eventueel een meta-tag met UTF-8
- set_charset() icm utf8, utf8mb4, of wat van toepassing is op je database of tabel
- data die ook echt ingevoerd is met bovenstaande instellingen
 
Nicos Vermeulen

Nicos Vermeulen

04/08/2020 15:23:12
Quote Anchor link
Thomas van den Heuvel op 03/08/2020 23:06:20:
Maar je tabel is utf8mb4, gebruik dan ook utf8mb4 als encoding, en niet utf8. utf8 is ook niet hetzelfde als UTF-8.

Probeer nadat je dit hebt aangepast de tekst eens opnieuw in te voeren. En dan niet via phpMyAdmin, maar via jouw applicatie, en controleer dan de HEX()-waarde opnieuw.

Volgende gedaan:
mysqli_set_charset($conn,"utf8mb4");

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

Nieuw record toegevoegd met het teken é.

Zie hem in de browser als: é
Hexadecimaal komt hij terug in de database als: C3A9

Met de volgende meta verwijzingen het zelfde resultaat:

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

In php met hex2bin de waarde weergegeven: hier komt ook terug: C3A9

Gebruik php versie 7.3.20

Daarna het volgende in mijn code gezet:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
header('Content-Type: text/html; charset=UTF-8');
?>


En toen zag het er goed uit!

Wat gebeurt hier nu? Ik had toch al met de metatags bovenaan in de head van de htmlcode gezegd dat het UTF-8 is?
Gewijzigd op 04/08/2020 15:23:34 door Nicos Vermeulen
 
Thomas van den Heuvel

Thomas van den Heuvel

04/08/2020 16:05:38
Quote Anchor link
Zucht.

MySQL heeft verschillende smaken voor UTF-8 zoals hierboven al eerder voorbij kwam: utf8, utf8mb4, utf16, utf16le, utf32 et cetera. Deze vormen alle "(deel)implementaties" van de universele standaard genaamd UTF-8.

utf8mb4 is MySQL-specifiek. Dit kun je dus niet instellen in je HTML-document als character encoding!

De huidige meta tag klopt dus niet, en was dus blijkbaar ook totaal niet van invloed op het eindresultaat. Het is (/wordt steeds) waarschijnlijk(er) dat er dus via de webserver een andere standaard character encoding staat ingesteld. Die heb je nu dus blijkbaar expliciet overschreven met je header()-aanroep in PHP.

Léés ook echt wat er staat:
Quote:
- eventueel een meta-tag met UTF-8

Ik schets een opzet die goed werkt, en probeer te motiveren waarom dit een van de makkelijkere manieren is, maar dit alles komt redelijk nauw. Je kunt niet zomaar strooien met allerlei termen en dan hopen dat het goedkomt.

Wat je dus, nogmaals, ten overvloede, zou moeten controleren, na het (tijdelijk) weghalen van deze header()-call is de character encoding die staat ingesteld in de response. Of raadpleeg de documentatie van je webserver, wat deze ook zou moge zijn, want hier heb je nog steeds niets over verteld.

Het is wat mij betreft echt heel erg belangrijk dat je begrijpt wat er gebeurt en ook wat er misgaat, dit is geen reeks van magische incantaties die je achtereenvolgens aanroept en dat het dan vervolgens miraculeus werkt. Alles heeft een specifieke betekenis en is van invloed op de werking.

Voordat je (grote hoeveelheden) data in je database gaat plempen moet dit alles kloppen en als een zonnetje lopen, want anders moet je straks een héleboel poep gaan schuiven om dit alles weer recht te breien.

Het is ook heeeeeeel belangrijk dat je set_charset() ook echt overal consequent aanroept. Dit dient te gebeuren direct nadat je een connectie maakt, en ook overal waar je een connectie maakt. Dus niet alleen in documenten waar je informatie uit de database ophoest, maar ook en vooral in de fragmenten waar je data naar de database wegschrijft.

Ik geef je nogmaals de link naar een eerdere reactie (interne link) waarin staat uitgelegd dat set_charset() in wezen het contract vormt tussen PHP en de database. Daarin staat beschreven dat jij de verantwoordelijkheid hebt om de data op de juiste manier aan te leveren, en vervolgens doet MySQL haar best om de data in de gewenste vorm terug te geven. Als jij je daar niet aan houdt, of andere regels en best practises voor character encoderingen met voeten treedt, dan kun je op geen redelijke manier verwachten dat dit het gewenste resultaat oplevert. Of als een oud collega van mij placht te zeggen: shit in, shit out.

Misschien ter verduidelijking: in de afgelopen reacties heb ik geprobeerd stap voor stap toe te werken naar het moment dat er dingen misgaan. Als in de database het karakter é de HEX-waarde C3A9 heeft, en als je met PHP deze opgehaalde waarde weergeeft met strtoupper(hex2bin($row['column'])) en ook C3A9 teruggeeft, dan houdt dat dus in dat:
- deze waarde op de juiste manier in de database zit;
- deze waarde op de juiste manier wordt opgehaald (zonder vertalingen tussen encoderingen).

Indien dit karakter vervolgens op de verkeerde wijze wordt weergegeven ligt dit 100% aan de wijze waarop het HTML-document wordt geserveerd.

Immers, de hexadecimale representatie van data staat los van enige character encoding, en dit is dus bij uitstek een geschikt middel om na te gaan waar er dingen misgaan / niet langer kloppen.

Dit schaakspel was dus een (hele) lange stapsgewijze eliminatie van dingen die fout kunnen gaan in dit proces.
Gewijzigd op 04/08/2020 16:28:57 door Thomas van den Heuvel
 
Nicos Vermeulen

Nicos Vermeulen

04/08/2020 17:11:18
Quote Anchor link
Thomas van den Heuvel op 04/08/2020 16:05:38:
utf8mb4 is MySQL-specifiek. Dit kun je dus niet instellen in je HTML-document als character encoding!

De huidige meta tag klopt dus niet, en was dus blijkbaar ook totaal niet van invloed op het eindresultaat. Het is (/wordt steeds) waarschijnlijk(er) dat er dus via de webserver een andere standaard character encoding staat ingesteld. Die heb je nu dus blijkbaar expliciet overschreven met je header()-aanroep in PHP.
Dag Thomas,

Ik waardeer het zeer dat je me wilt helpen, en zo veel geduld hebt. Ik hoop dat je begrijpt dat als je niet helemaal thuis bent in de materie van charactersets in combinatie met mySQL dat dit niet makkelijk is.

Ben bang dat ik zelf niet bij de standaard character encoding van de webserver kan om deze aan te passen. Het is een Apache webserver welke ik kan beheren via Plesk. Heb al zitten zoeken maar kan de character encoding niet vinden. Denk dat ik dan bij mijn hoster moet zijn. Zag wel dat ik Additional headers kan toevoegen. Of zou dit in de htaccess file moeten?
Gewijzigd op 04/08/2020 17:18:12 door Nicos Vermeulen
 
- Ariën  -
Beheerder

- Ariën -

04/08/2020 17:35:44
Quote Anchor link
Dit kan je inderdaad via .htaccess doen.
Maar met meta-tags en header() heb je, los van de database, al de juiste basis. Let ook op je encoding van je bestand.

Pobeer eerst even een statische mock-up uit te werken, voordat je wat aan het aanklooien bent.
Gewijzigd op 04/08/2020 17:48:52 door - Ariën -
 
Thomas van den Heuvel

Thomas van den Heuvel

04/08/2020 19:51:22
Quote Anchor link
Quote:
Ik waardeer het zeer dat je me wilt helpen, en zo veel geduld hebt. Ik hoop dat je begrijpt dat als je niet helemaal thuis bent in de materie van charactersets in combinatie met mySQL dat dit niet makkelijk is.

Ik weet dat dit niet eenvoudig is, het heeft mij zelf ook enige tijd gekost. Maar als je dan eenmaal doorhebt wat er allemaal speelt, dan heb je hier in het vervolg waarschijnlijk een stuk minder problemen mee, of is er in ieder geval een stuk grotere bewustwording.

Je had alles in principe al voor elkaar binnen PHP. Dat lijkt mij bij uitstek de plek om dit te regelen, zodat je hier binnen de code zelf rechtstreeks invloed op hebt.

Ik zou dit niet in .htaccess stoppen, omdat dat op die manier al snel een vuilnisbak wordt voor allerlei dingen die je elders, en wat mij bereft op die manier ook netter, kunt regelen. Ook het beheerpaneel is misschien geen goede plek. Beter lijkt mij dat je dit op alle plaatsen expliciet instelt, en het daarmee ook expliciet gedefinieerd is. Dit is ook veel meer "in het zicht" dan dat het weggestopt zit in een configuratiebestand of webserverinstelling.

Gebruik dus gewoon een header() zoals voorheen, als dat werkt. Hiermee heb je op één plek - in PHP zelf - de volledige controle over de output, op het moment dat deze output gegenereerd wordt.
Gewijzigd op 04/08/2020 20:11:05 door Thomas van den Heuvel
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.