Ως προγραμματιστής PHP ή MySQL Μόλις προχωρήσετε πέρα από τα όρια των άνετων σετ χαρακτήρων μόνο στα Αγγλικά, θα βρεθείτε γρήγορα μπερδεμένοι στον υπέροχα παράξενο κόσμο του UTF-8.
Ένα αστάρι γρήγορης εμφάνισης UTF-8Σε ένα εργασία Πριν από αυτό, αρχίσαμε να αντιμετωπίζουμε προβλήματα κωδικοποίησης δεδομένων όταν εμφανίζουμε βιογραφίες καλλιτεχνών από όλο τον κόσμο. Σύντομα έγινε εμφανές ότι υπήρχαν προβλήματα με τα αποθηκευμένα δεδομένα, καθώς μερικές φορές τα δεδομένα κωδικοποιήθηκαν σωστά και μερικές φορές όχι.
Αυτό οδήγησε τους προγραμματιστές να εφαρμόσουν ένα μείγμα ενημερώσεων κώδικα, μερικές φορές με JavaScript, μερικές φορές με μετα-ετικέτες χαρακτήρων HTML, μερικές φορές με PHP και ούτω καθεξής. Σύντομα, καταλήξαμε σε μια λίστα 600.000 βιογραφικών καλλιτεχνών, με τις πληροφορίες διπλής ή τριπλής κωδικοποίησης, με δεδομένα αποθηκευμένα με διαφορετικούς τρόπους, ανάλογα με το ποιος είχε προγραμματίσει τη λειτουργία ή εφάρμοσε την ενημέρωση κώδικα. Μια κλασική τεχνική φωλιά αρουραίου.
Στην πραγματικότητα, η πλοήγηση σε θέματα κωδικοποίησης δεδομένων UTF-8 μπορεί να είναι μια απογοητευτική εμπειρία. Αυτή η ανάρτηση παρέχει ένα συνοπτικό «βιβλίο μαγειρικής» για την αντιμετώπιση αυτών των ζητημάτων όταν εργάζεστε με PHP και MySQL ειδικότερα, με βάση την πρακτική εμπειρία και τα διδάγματα που αντλήθηκαν (και με ευχαριστίες, εν μέρει, σε πληροφορίες που ανακαλύφθηκαν εδώ Υ εδώ στο μονοπάτι).
Συγκεκριμένα, θα καλύψουμε τα ακόλουθα σε αυτήν την ανάρτηση:
Το πρώτο πράγμα που πρέπει να κάνετε είναι να τροποποιήσετε το αρχείο 'php.ini' για να χρησιμοποιήσετε το UTF-8 ως το προεπιλεγμένο σύνολο χαρακτήρων:
default_charset = 'utf-8';
( Σημείωση: Μπορείτε αργότερα να χρησιμοποιήσετε το phpinfo () για να επιβεβαιώσετε ότι έχει ρυθμιστεί σωστά ).
Εντάξει, τώρα PHP και UTF-8 θα πρέπει να λειτουργούν καλά μαζί. Αλήθεια?
Λοιπόν, όχι ακριβώς. Στην πραγματικότητα, δεν είναι καν κοντά στο να το κάνουν.
Παρόλο που αυτή η αλλαγή θα διασφαλίσει ότι η PHP εξάγει πάντα το UTF-8 ως κωδικοποίηση χαρακτήρων (σε κεφαλίδες τύπου - περιεχόμενο απόκρισης προγράμματος περιήγησης), πρέπει να κάνετε ορισμένες τροποποιήσεις στον κώδικα PHP σας, για να διασφαλίσετε ότι επεξεργάζεται και δημιουργεί σωστά χαρακτήρες UTF-8 .
Σχετίζεται με: Βέλτιστες πρακτικές και συμβουλές PHP από ApeeScape DevelopersΓια να βεβαιωθείτε ότι ο κωδικός PHP σας χειρίζεται καλά στο περιβάλλον δοκιμών κωδικοποίησης δεδομένων UTF-8, ακολουθούν τα πράγματα που πρέπει να κάνετε:
Ορίστε το UTF-8 ως σύνολο χαρακτήρων για όλες τις εξόδους κεφαλίδας με τον κωδικό PHP.
Σε κάθε κεφαλίδα εξόδου PHP, καθορίστε το UTF-8 ως την κωδικοποίηση:
κεφαλίδα («Τύπος περιεχομένου: κείμενο / html; charset = utf-8»);
Καθορίστε το UTF-8 ως τον τύπο κωδικοποίησης για XML
function utf8_for_xml($string) { return preg_replace('/[^x{0009}x{000a}x{000d}x{0020}-x{D7FF}x{E000}-x{FFFD}]+/u', ' ', $string); }
Κατάργηση μη υποστηριζόμενων χαρακτήρων από το XML
Επειδή δεν γίνονται αποδεκτοί όλοι οι χαρακτήρες UTF-8 σε ένα έγγραφο XML, πρέπει να καταργήσετε οποιονδήποτε τύπο χαρακτήρων από οποιοδήποτε XML που δημιουργείτε. Μια χρήσιμη λειτουργία για να το κάνω αυτό (το οποίο βρήκα εδώ) είναι η ακόλουθη:
$safeString = utf8_for_xml($yourUnsafeString);
Δείτε πώς μπορείτε να χρησιμοποιήσετε αυτήν τη λειτουργία στον κώδικά σας:
htmlspecialchars($str, ENT_NOQUOTES, 'UTF-8')
Καθορίστε το UTF-8 ως σύνολο χαρακτήρων για όλο το περιεχόμενο HTML
Για περιεχόμενο HTML, καθορίστε το UTF-8 ως κωδικοποίηση:
default_charset
Σε φόρμες HTML, καθορίστε το UTF-8 ως την κωδικοποίηση:
htmlspecialchars
Καθορίστε το UTF-8 ως την κωδικοποίηση για όλες τις κλήσεις σε htmlspecialchars
Για παράδειγμα:
htmlentities
Σημείωση: Από PHP 5.6.0, η τιμή mysql_set_charset
χρησιμοποιείται από προεπιλογή. Από το PHP 5.4.0, το UTF-8 ήταν το προεπιλεγμένο, αλλά πριν από το PHP 5.4.0, το ISO-8859-1 χρησιμοποιήθηκε ως προεπιλογή. Επομένως, είναι καλή ιδέα να ορίζετε πάντα ρητά το UTF-8, να είστε ασφαλείς, παρόλο που αυτό το επιχείρημα είναι τεχνικά προαιρετικό.
Σημειώστε επίσης ότι, για UTF-8, $link = mysql_connect('localhost', 'user', 'password'); mysql_set_charset('utf8', $link);
Υ mysql_set_charset
μπορούν να χρησιμοποιηθούν εναλλακτικά.
Καθορίστε το UTF-8 ως το προεπιλεγμένο σύνολο χαρακτήρων που θα χρησιμοποιείται κατά την ανταλλαγή δεδομένων με τη βάση δεδομένων MySQL, χρησιμοποιώντας mysqli::set_charset
:
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'test'); /* check connection */ if (mysqli_connect_errno()) { printf('Connect failed: %s
', mysqli_connect_error()); exit(); } /* change character set to utf8 */ if (!$mysqli->set_charset('utf8')) { printf('Error loading character set utf8: %s
', $mysqli->error); } else { printf('Current character set: %s
', $mysqli->character_set_name()); } $mysqli->close();
Σημειώστε ότι από την PHP 5.5.0, iconv
έχει καταργηθεί και iconv_strlen
θα πρέπει να χρησιμοποιηθεί αντ 'αυτού:
mbstring
Υπάρχουν πολλές συναρτήσεις PHP που ενδέχεται να αποτύχουν ή τουλάχιστον να μην συμπεριφέρονται όπως αναμένεται εάν η αναπαράσταση χαρακτήρων χρειάζεται περισσότερα από 1 byte (όπως κάνει το UTF-8). Ένα παράδειγμα είναι η συνάρτηση strlen, η οποία θα επιστρέψει τον αριθμό των byte αντί για τον αριθμό των χαρακτήρων.
Υπάρχουν δύο διαθέσιμες επιλογές για την αντιμετώπιση αυτού:
Οι λειτουργίες [mysql] default-character-set=UTF-8 [mysqld] character-set-client-handshake = false #force encoding to uft8 character-set-server=UTF-8 collation-server=UTF-8_general_ci [mysqld_safe] default-character-set=UTF-8
διατίθεται από προεπιλογή με PHP, παρέχει συμβατές εκδόσεις πολλών byte πολλών από αυτές τις λειτουργίες (για παράδειγμα, my.ini
, κ.λπ.). Ωστόσο, να θυμάστε ότι οι χορδές που παρέχετε σε αυτές τις λειτουργίες πρέπει με τη σειρά τους να κωδικοποιούνται σωστά.
Υπάρχει επίσης η επέκταση mysql> show variables like 'char%';
στην PHP (διατίθενται πληροφορίες σχετικά με την ενεργοποίηση και τη διαμόρφωση εδώ ). Αυτή η επέκταση παρέχει ένα πλήρες σύνολο λειτουργιών που ικανοποιεί επαρκώς την κωδικοποίηση πολλαπλών byte.
Από την πλευρά των πραγμάτων MySQL / UTF-8, απαιτούνται τροποποιήσεις στο αρχείο my.ini ως εξής:
Ορίστε τις ακόλουθες παραμέτρους διαμόρφωσης μετά από κάθε αντίστοιχη ετικέτα: [client] default-character-set = UTF-8
| character_set_client | UTF-8 | character_set_connection | UTF-8 | character_set_database | UTF-8 | character_set_filesystem | binary | character_set_results | UTF-8 | character_set_server | UTF-8 | character_set_system | UTF-8 | character_sets_dir | /usr/share/mysql/charsets/
Αφού πραγματοποιήσετε τις παραπάνω αλλαγές στο αρχείο set names UTF-8;
, κάντε επανεκκίνηση του δαίμονα MySQL.
Για να επαληθεύσετε ότι όλα έχουν ρυθμιστεί σωστά για χρήση κωδικοποίησης UTF-8, εκτελέστε το ακόλουθο ερώτημα:
sphinx.conf
Το αποτέλεσμα πρέπει να είναι κάπως έτσι:
charset_type = utf-8
Αν αντίθετα βλέπετε το latin1 για οποιοδήποτε από αυτά, ελέγξτε τη διαμόρφωσή σας και βεβαιωθείτε ότι έχετε επανεκκινήσει με επιτυχία το MySQL Daemon.
Το MySQL UTF-8 είναι στην πραγματικότητα μια μερική εφαρμογή του συνόλου χαρακτήρων UTF-8. Συγκεκριμένα, η κωδικοποίηση δεδομένων MySQL UTF-8 χρησιμοποιεί το πολύ 3 byte, ενώ 4 byte απαιτούνται για την κωδικοποίηση του πλήρους συνόλου χαρακτήρων UTF-8. Αυτό είναι καλό για όλους τους χαρακτήρες στη γλώσσα, αλλά εάν πρέπει να υποστηρίξετε αστρικά σύμβολα (των οποίων τα σημεία κώδικα κυμαίνονται από U + 010000 έως U + 10FFFF), απαιτούν κωδικοποίηση τεσσάρων byte που δεν μπορεί να υποστηριχθεί στο MySQL UTF-8. Στο MySQL 5.5 0.3, αυτό συζητήθηκε με την προσθήκη υποστήριξης σετ χαρακτήρων utf8mb4 , που χρησιμοποιεί το πολύ τέσσερα byte ανά χαρακτήρα και επομένως υποστηρίζει το πλήρες σύνολο χαρακτήρων UTF-8. Έτσι, εάν χρησιμοποιείτε MySQL 5.5.3 ή μεταγενέστερη έκδοση, χρησιμοποιήστε το utf8mb4 αντί για το UTF-8 ως σύνολο χαρακτήρων βάσης δεδομένων / πίνακα / σειράς. Περισσότερες πληροφορίες διατίθενται εδώ.
Εάν ο πελάτης σύνδεσης δεν έχει τρόπο να καθορίσει την κωδικοποίηση για την επικοινωνία του με τη MySQL, μόλις ολοκληρωθεί η σύνδεση, ίσως χρειαστεί να εκτελέσετε την ακόλουθη εντολή / ερώτημα:
sql_query_pre = SET CHARACTER_SET_RESULTS=UTF-8
Κατά τον προσδιορισμό του μεγέθους των πεδίων varchar κατά τη μοντελοποίηση της βάσης δεδομένων σας, μην ξεχνάτε ότι οι χαρακτήρες UTF-8 μπορούν να απαιτούν έως και 4 byte ανά χαρακτήρα.
Στο αρχείο διαμόρφωσης Sphinx (δηλαδή sql_query_pre = SET NAMES UTF-8
):
Ορίστε τον ορισμό ευρετηρίου για να έχει:
charset_table
Προσθέστε τα ακόλουθα στον ορισμό της γραμματοσειράς σας:
ALTER SCHEMA `your-db-name` DEFAULT CHARACTER SET UTF-8;
mysql> show variables like 'char%';
Επανεκκινήστε τον κινητήρα και επαναλάβετε όλα τα ευρετήρια.
Εάν θέλετε να διαμορφώσετε το Sphynx έτσι ώστε γράμματα όπως C c Ć ć Ĉ ĉ ċ Č č να αντιμετωπίζονται ως τα ίδια για σκοπούς αναζήτησης, θα πρέπει να διαμορφώσετε ένα mysqldump -u USERNAME -pDB_PASSWORD --opt --skip-set-charset --default-character-set=latin1 --skip-extended-insert DATABASENAME --tables TABLENAME > DUMP_FILE_TABLE.sql
(επίσης γνωστό ως αναδίπλωση χαρακτήρων) που είναι ουσιαστικά μια χαρτογράφηση μεταξύ χαρακτήρων. Περισσότερες πληροφορίες είναι διαθέσιμες εδώ .
Εάν έχετε ήδη υπάρχουσα βάση δεδομένων που έχει ήδη κωδικοποιηθεί στο latin1, δείτε πώς μπορείτε να μετατρέψετε το latin1 σε UTF-8:
Βεβαιωθείτε ότι έχετε πραγματοποιήσει όλες τις τροποποιήσεις στις ρυθμίσεις διαμόρφωσης στο αρχείο my.ini, όπως περιγράφεται παραπάνω.
Εκτελέστε την ακόλουθη εντολή:
mysqldump -u root --opt --skip-set-charset --default-character-set=latin1 --skip-extended-insert artists-database --tables tbl_artist > tbl_artist.sql
Μέσω της γραμμής εντολών, ελέγξτε ότι όλα έχουν ρυθμιστεί σωστά για το UTF-8
perl -i -pe 's/DEFAULT CHARSET=latin1/DEFAULT CHARSET=UTF-8/' DUMP_FILE_TABLE.sql
Δημιουργήστε ένα αρχείο dump σε κωδικοποίηση latin1 για τον πίνακα που θέλετε να μετατρέψετε:
mysql> source 'DUMP_FILE_TABLE.sql';
Παράδειγμα:
mysql> select count(*) from MY_TABLE where LENGTH(MY_FIELD) != CHAR_LENGTH(MY_FIELD);
Κάντε μια καθολική αναζήτηση και αντικαταστήστε το σύνολο χαρακτήρων στο αρχείο dump από latin1 σε UTF-8:
Για παράδειγμα, χρησιμοποιώντας Perl:
create table temptable ( select * from MY_TABLE where LENGTH(MY_FIELD) != CHAR_LENGTH(MY_FIELD));
Σημείωση για χρήστες Windows: Αυτή η συμβολοσειρά αντικατάστασης χαρακτήρων (latin1 έως UTF-8) μπορεί επίσης να γίνει χρησιμοποιώντας εύρεση και αντικατάσταση στο WordPad (ή σε κάποιο άλλο πρόγραμμα επεξεργασίας κειμένου, όπως το vim). Βεβαιωθείτε ότι έχετε αποθηκεύσει το αρχείο ως έχει (όχι ως αρχείο κειμένου Unicode!).
Από εδώ και πέρα, θα αρχίσουμε να βάζουμε τα δεδομένα της βάσης δεδομένων, οπότε θα ήταν πιθανό να είναι συνετό να δημιουργήσετε αντίγραφα ασφαλείας της βάσης δεδομένων, εάν δεν το έχετε κάνει ήδη. Στη συνέχεια, επαναφέρετε την απόρριψη στη βάση δεδομένων:
alter table temptable modify temptable.ArtistName varchar(128) character set latin1;
Βρείτε τυχόν εγγραφές που δεν μετατράπηκαν σωστά και διορθώστε τις. Δεδομένου ότι οι χαρακτήρες που δεν είναι ASCII είναι πολυ bytes βάσει σχεδίασης, μπορούμε να τους βρούμε συγκρίνοντας το μήκος byte με το μήκος χαρακτήρων (δηλαδή για τον προσδιορισμό σειρών που μπορούν να περιέχουν χαρακτήρες διπλού UTF-8). Κωδικοποιημένοι που πρέπει να διορθωθούν).
Δείτε εάν υπάρχουν εγγραφές με χαρακτήρες πολλαπλών byte (εάν αυτό το ερώτημα επιστρέφει μηδέν, τότε δεν θα πρέπει να υπάρχουν εγγραφές με χαρακτήρες πολλαπλών byte στον πίνακα και μπορείτε να προχωρήσετε στο Βήμα 8).
ArtistName
Αντιγράψτε σειρές με χαρακτήρες πολλαπλών byte σε έναν προσωρινό πίνακα:
alter table temptable modify temptable.ArtistName blob; alter table temptable modify temptable.ArtistName varchar(128) character set UTF-8;
Μετατρέπει διπλούς κωδικοποιημένους χαρακτήρες UTF-8 σε κατάλληλους χαρακτήρες UTF-8.
Αυτό είναι λίγο δύσκολο. Μια συμβολοσειρά διπλής κωδικοποίησης είναι αυτή που έχει κωδικοποιηθεί σωστά ως UTF-8. Ωστόσο, η MySQL μας έκανε τότε το λάθος εύρημα να το μετατρέψουμε (από αυτό που νόμιζε ότι ήταν latin1) σε UTF-8 ξανά, όταν θέσαμε τη στήλη σε κωδικοποίηση UTF-8. Η επίλυση αυτού, επομένως, απαιτεί μια διαδικασία δύο βημάτων μέσω της οποίας «εξαπατάμε» τη MySQL για να αποτρέψουμε να μας κάνει αυτή τη «χάρη».
Αρχικά, ορίζουμε τον τύπο κωδικοποίησης για τη στήλη πίσω στο latin1, εξαλείφοντας έτσι τη διπλή κωδικοποίηση:
Παράδειγμα:
delete from MY_TABLE where LENGTH(MY_FIELD) = CHAR_LENGTH(MY_FIELD);
Σημείωση: Βεβαιωθείτε ότι χρησιμοποιείτε τον σωστό τύπο πεδίου για τον πίνακα σας. Στο παραπάνω παράδειγμα, για τον πίνακα μας, ο σωστός τύπος πεδίου για replace into MY_TABLE (select * from temptable);
ήταν varchar (128), αλλά το πεδίο πίνακα θα μπορούσε να είναι κείμενο ή οποιοσδήποτε άλλος τύπος. Βεβαιωθείτε ότι το καθορίσατε σωστά.
Το πρόβλημα είναι ότι τώρα, αν ρυθμίσουμε την κωδικοποίηση στήλης σε UTF-8, η MySQL θα εκτελέσει ξανά την κωδικοποίηση δεδομένων latin1 έως UTF-8 και θα επιστρέψουμε στο σημείο που ξεκινήσαμε. Για να αποφευχθεί αυτό, ο τύπος στήλης αλλάζει σε blob και στη συνέχεια ορίζεται σε UTF-8. Αυτό εκμεταλλεύεται το γεγονός ότι η MySQL δεν θα επιχειρήσει να κωδικοποιήσει ένα blob. Έτσι, μπορούμε να «εξαπατήσουμε» τη μετατροπή σετ χαρακτήρων MySQL, για να αποφύγουμε το πρόβλημα διπλής κωδικοποίησης.
Παράδειγμα:
|_+_|
(Και πάλι, όπως σημειώθηκε παραπάνω, φροντίστε να χρησιμοποιήσετε τον κατάλληλο τύπο πεδίου για τον πίνακα σας.)
Διαγραφή σειρών με μόνο χαρακτήρες ενός byte που ανήκουν στον προσωρινό πίνακα:
Εισαγάγετε ξανά τις σταθερές σειρές στον αρχικό πίνακα (πριν το κάνετε αυτό, πρέπει να εκτελέσετε ορισμένες επιλογές στον προσωρινό πίνακα για να επαληθεύσετε ότι έχει διορθωθεί σωστά, ακριβώς ως προφύλαξη).
|_+_|
Ένα άλλο πράγμα που πρέπει να θυμάστε και να ελέγξετε είναι ότι τα αρχεία πηγαίου κώδικα, τα αρχεία πόρων και ούτω καθεξής, αποθηκεύονται σωστά με κωδικοποίηση δεδομένων UTF-8. Διαφορετικά, ενδέχεται να μην γίνεται σωστή διαχείριση όλων των 'ειδικών' χαρακτήρων σε αυτά τα αρχεία.
Στο Netbeans, για παράδειγμα, μπορείτε να κάνετε δεξί κλικ στο έργο σας, να επιλέξετε ιδιότητες και στη συνέχεια στην ενότητα 'Πηγές', θα βρείτε την επιλογή κωδικοποίησης δεδομένων (συνήθως από προεπιλογή είναι UTF-8, αλλά είναι καλύτερο να το ελέγξετε).
Ή στο Σημειωματάριο των Windows, χρησιμοποιήστε την επιλογή 'Αποθήκευση ως ...' στο μενού Αρχεία και επιλέξτε την επιλογή κωδικοποίησης UTF-8 στο κάτω μέρος του πλαισίου διαλόγου. (Σημειώστε ότι η επιλογή 'Unicode' που προσφέρει το Σημειωματάριο είναι στην πραγματικότητα UTF-16 και δεν είναι αυτό που θέλετε.)
Αν και μπορεί να είναι κάπως κουραστικό, αφιερώνοντας χρόνο για να αναθεωρήσετε αυτά τα βήματα για τη συστηματική αντιμετώπιση των προβλημάτων κωδικοποίησης δεδομένων MySQL και PHP UTF-8 μπορεί να σας εξοικονομήσει πολύ χρόνο. Μακροπρόθεσμα, αυτό το είδος μεθοδικής προσέγγισης είναι πολύ ανώτερο από την κοινή τάση για επιδιόρθωση του συστήματος.
Ελπίζω ότι αυτός ο οδηγός υπογραμμίζει τη σημασία του να λαμβάνεται υπόψη ο ορισμός του συνόλου δεδομένων κατά την αρχική ρύθμιση ενός περιβάλλοντος έργου και να εργάζεται σε ένα περιβάλλον έργου λογισμικού που λαμβάνει υπόψη την κωδικοποίηση χαρακτήρων κατά τον χειρισμό κειμένου και συμβολοσειρών.
Σχετίζεται με: Πριν από τον εντοπισμό σφαλμάτων PHP που δεν λειτουργεί, συμβουλευτείτε αυτήν τη λίστα με τα 10 πιο συνηθισμένα λάθη που κάνουν οι προγραμματιστές της PHP, πριν από τον εντοπισμό σφαλμάτων PHP που δεν λειτουργεί, ελέγξτε τη λίστα με τα 10 πιο κοινά λάθη που κάνουν οι προγραμματιστές της PHP.