Κανονικοποιητής Unicode

Το Unicode έχει τέσσερις μορφές κανονικοποίησης και δεν είναι εναλλάξιμες. Το NFC και το NFD αναδιατάσσουν τον ίδιο χαρακτήρα μεταξύ ενός σημείου κώδικα και βάσης με συνδυαστικό σημάδι· το NFKC και το NFKD πηγαίνουν παραπέρα και ξαναγράφουν χαρακτήρες που απλώς μοιάζουν μεταξύ τους — το σύνδεσμος fi, το πλήρους πλάτους A, το κυκλωμένο ①, ο λατινικός αριθμός Ⅻ. Η σελίδα περνά το ίδιο κείμενο και από τις τέσσερις, τυπώνει μία γραμμή ανά χαρακτήρα που δηλώνει τι άλλαξε και γιατί, μετρά γραφήματα έναντι σημείων κώδικα και bytes UTF-8, και συγκρίνει δύο συμβολοσειρές με τη μορφή που επιλέγεται. Το δεύτερο μισό αφορά τους χαρακτήρες που δεν εμφανίζονται καθόλου: κενά μηδενικού πλάτους, συνδέσμους, αμφίδρομους ελέγχους, επιλογείς παραλλαγής, χαρακτήρες ελέγχου C0 και C1, σημεία κώδικα ιδιωτικής χρήσης και σπασμένα ψευδο-χαρακτήρες, καθένας με τη θέση και το όνομά του, και τη δυνατότητα αφαίρεσής τους από αντίγραφο του κειμένου.
Όλη η επεξεργασία γίνεται τοπικά στο πρόγραμμα περιήγησης — τα δεδομένα δεν ανεβαίνουν ποτέ σε κανέναν διακομιστή.

Δεν είναι σαφές τι είναι η κανονικοποίηση; Η κανονικοποίηση Unicode εξηγείται με απλά λόγια — γιατί το é μπορεί να είναι ένας ή δύο χαρακτήρες, και γιατί αυτό χαλάει μια είσοδο.
Μορφή για αναφορά και σύγκριση:
Σύγκριση των δύο πλαισίων μετά από:
NFC (τίποτα ακόμη)
NFD (τίποτα ακόμη)
NFKC (τίποτα ακόμη)
NFKD (τίποτα ακόμη)
Δεν έχει γίνει σύγκριση ακόμη. Επικόλληση των δύο συμβολοσειρών και πάτημα του Σύγκριση των δύο συμβολοσειρών.
πρώτη συμβολοσειρά, κανονικοποιημένη (τίποτα ακόμη)
δεύτερη συμβολοσειρά, κανονικοποιημένη (τίποτα ακόμη)
Δεν έχει γίνει σάρωση ακόμη. Πάτημα του Σάρωση για αόρατους και επικίνδυνους χαρακτήρες, και έπειτα του Αφαίρεση αν επιθυμείται το κείμενο χωρίς αυτούς τους χαρακτήρες.
κείμενο χωρίς τις επιλεγμένες κατηγορίες (τίποτα ακόμη)
Το Ctrl+Enter στο πρώτο πλαίσιο εκτελεί την ενεργή λειτουργία.
Επιλογή λειτουργίας και πάτημα του κουμπιού της. Κανονικοποίηση γράφει και τις τέσσερις μορφές του κειμένου και μία γραμμή ανά χαρακτήρα· η Σύγκριση δηλώνει αν δύο συμβολοσειρές είναι ίδιες μετά την κανονικοποίηση· η Σάρωση καταγράφει τους χαρακτήρες που δεν φαίνονται και μπορεί να τους αφαιρέσει. Το κουμπί Αυτοτέστ επανεκτελεί κάθε τεκμηριωμένη περίπτωση στο αρχείο δεδομένων αυτής της σελίδας, κάθε χαρακτήρα που ονομάζουν οι πίνακες αόρατων και ελέγχου, και αναφέρει το πλήθος εδώ.
Κάθε χαρακτήρας, μία γραμμή για κάθε έναν:τι είναι ο χαρακτήρας, τι κάνουν σε αυτόν οι τέσσερις μορφές, και γιατί. Μια σκιασμένη γραμμή είναι χαρακτήρας που αλλάζει τουλάχιστον μία μορφή· η στήλη NFD δείχνει σε τι αποσυντίθεται ο χαρακτήρας, όταν αποσυντίθεται.
Πόσους χαρακτήρες είναι αυτό;Το ίδιο κείμενο μετρημένο τέσσερις τρόπους: συμπλέγματα γραφημάτων (αυτό που μετρά ο αναγνώστης), σημεία κώδικα (αυτό που ορίζει το Unicode), μονάδες UTF-16 (αυτό που δείκτη η JavaScript) και bytes UTF-8 (αυτό που ταξιδεύει). Η κανονικοποίηση μετακινεί τους αριθμούς ανάμεσα στη δεύτερη και την τρίτη στήλη χωρίς να αλλάζει την πρώτη.
Τι βρέθηκε, με τη σειρά που εμφανίζεται:η θέση μετρά σημεία κώδικα, η κατηγορία είναι αυτή στην οποία ανήκει ο χαρακτήρας, και η σημείωση λέει τι κάνει αυτός ο χαρακτήρας σε ό,τι διαβάζει το κείμενο στη συνέχεια.
Οι κατηγορίες που αντιμετωπίζει η σελίδα ως ύποπτες:ο πίνακας εκτυπώνεται από τον ίδιο τον σαρωτή, οπότε τα λόγια αυτής της σελίδας και η συμπεριφορά των κουμπιών δεν μπορούν να αποκλίνουν. Η σοβαρότητα αφορά την επίδραση σε μια σύγκριση, όχι το πόσο πιθανό είναι ο χαρακτήρας να είναι αθώος.
Οι χαρακτήρες ιδιωτικής χρήσης σημειώνονται ως ύποπτοι για διαφορετικό λόγο από τους υπόλοιπους: αποδίδονται κανονικά, απλώς δεν έχουν συμφωνημένη σημασία, οπότε το ίδιο σημείο κώδικα είναι διαφορετική εικόνα σε κάθε γραμματοσειρά που το ορίζει.
Πόσο μεγάλη είναι η είσοδος που δέχεται αυτή η σελίδα, και γιατί:η κανονικοποίηση είναι ανασύνθεση ολόκληρης της συμβολοσειράς, και η σελίδα εκτελεί πολλές τέτοιες μαζί με σάρωση και καταμέτρηση γραφημάτων, οπότε το κόστος αυξάνεται γραμμικά με το μήκος της εισόδου. Τα παρακάτω όρια υπάρχουν επειδή ο πίνακας ανά χαρακτήρα σχεδιάζεται στο έγγραφο, και ένας πίνακας με ένα εκατομμύριο γραμμές είναι αυτό που πραγματικά σταματά έναν περιηγητή.
Τι είναι η κανονικοποίηση Unicode;

Το Unicode επιτρέπει περισσότερους από έναν τρόπους γραφής ορισμένων χαρακτήρων. Το γράμμα é μπορεί να είναι ένα μοναδικό σημείο κώδικα, το U+00E9, ή ένα απλό e ακολουθούμενο από συνδυαστικό οξεία, U+0065 U+0301. Οι δύο φαίνονται ίδιες στην οθόνη, τυπώνονται το ίδιο, και ως συμβολοσειρές είναι διαφορετικές: έχουν διαφορετικά μήκη, διαφορετικά bytes UTF-8, και το 'é' === 'é' είναι false.

Δεν είναι περιέργεια, είναι κατηγορία σφάλματος. Το αν παίρνει κανείς τη μία ή την άλλη μορφή εξαρτάται από τη διάταξη πληκτρολογίου, το λειτουργικό σύστημα, τη μέθοδο εισαγωγής και από το αν το κείμενο αντιγράφηκε ποτέ μέσω επεξεργαστή κειμένου. Ένας έλεγχος κωδικού πρόσβασης, ένας μοναδικός δείκτης βάσης δεδομένων, μια λίστα αποκλεισμού ή ένα κλειδί cache που συγκρίνει συμβολοσειρές byte προς byte θα αντιμετωπίσει τις δύο γραφές ως δύο διαφορετικές τιμές — έτσι ένας χρήστης μπορεί να ορίσει κωδικό σε μια συσκευή και να μην μπορεί να συνδεθεί από άλλη, και η ίδια λέξη μπορεί να εμφανιστεί δύο φορές σε λίστα ονομάτων «ήδη κατειλημμένων».

Η εκδοχή των 30 δευτερολέπτων: NFC και NFD είναι το ίδιο κείμενο γραμμένο με δύο τρόπους — το NFC συνθέτει (το e + τόνος γίνεται ένας χαρακτήρας), το NFD αποσυνθέτει (ένας χαρακτήρας γίνεται e + τόνος). Προορίζονται να είναι χωρίς απώλειες και είναι οι δύο μορφές που χρησιμοποιούνται για σύγκριση και αποθήκευση. Το NFKC και το NFKD κάνουν ό,τι κάνουν οι δύο πρώτες και στη συνέχεια αντικαθιστούν και τους χαρακτήρες συμβατότητας: ο σύνδεσμος fi γίνεται δύο γράμματα, το ½ γίνεται 1⁄2, ο λατινικός αριθμός ⅲ γίνεται XII, ένα πλήρους πλάτους A γίνεται απλό A. Αυτό ακριβώς θέλει ένα ευρετήριο αναζήτησης ή ένα φίλτρο λέξεων-κλειδιών, και αυτό ακριβώς δεν πρέπει ποτέ να εφαρμόζεται σε κωδικό πρόσβασης.
Γιατί τέσσερις μορφές και όχι μία

Ο διαχωρισμός υπάρχει επειδή εξυπηρετούνταν δύο διαφορετικοί στόχοι. Η σύνθεση και η αποσύνθεση κειμένου είναι κανονική αλλαγή: το αποτέλεσμα σημαίνει το ίδιο και, για τις δύο πρώτες μορφές, δεν χάνεται τίποτα. Η σύμπτυξη προσθέτει σε συνδέσμους, εκθέτες, κυκλωμένα γράμματα και μορφές πλήρους πλάτους είναι αλλαγή συμβατότητας: γίνεται για ευκολία αντιστοίχισης, και καταστρέφει πράγματι τη διάκριση ανάμεσα σε δύο συμβολοσειρές που το Unicode λέει ότι είναι διαφορετικές. Οι τέσσερις μορφές είναι κάθε συνδυασμός των δύο αποφάσεων.

Ο πίνακας παραπάνω είναι η λίστα περιπτώσεων του αρχείου δεδομένων αυτής της σελίδας, ελεγμένη έναντι του δικού του String.prototype.normalize της πλατφόρμας, και το Αυτοτέστ ελέγχει ξανά κάθε γραμμή του μπροστά στον χρήστη. Οι δύο πρώτες γραμμές διαβάζονται μαζί: είναι το ίδιο κείμενο γραμμένο με δύο τρόπους, και μετά την κανονικοποίηση είναι η ίδια συμβολοσειρά.

Παραδείγματα βήμα προς βήμα

Αυτές είναι οι συμβολοσειρές από τις οποίες περνά το κουμπί Δείγμα, κανονικοποιημένες ζωντανά από τη σελίδα αντί να πληκτρολογηθούν ως απαντήσεις. Κάθε μία επιλέχθηκε επειδή μια μορφή της κάνει κάτι ορατό.

Χαρακτήρες συμβατότητας: τι κοστίζει το NFKC

Το NFKC και το NFKD είναι με απώλειες, και η απώλεια είναι σκόπιμη. Απαντούν στο ερώτημα «είναι η ίδια λέξη» και όχι στο ερώτημα «είναι η ίδια συμβολοσειρά». Απορρέουν τρεις συνέπειες, και όλες είναι εύκολο να γίνουν λάθος σε κώδικα παραγωγής:

  • Ποτέ δεν αποθηκεύεται κωδικός πρόσβασης, διακριτικό ή υπογραφή σε μορφή συμβατότητας. Δύο διαφορετικές εισόδους θα συμπτύσσονταν στην ίδια αποθηκευμένη τιμή, κάτι που μετατρέπει έναν έλεγχο μήκους ή χαρακτήρων σε πιο αδύναμο. Οι κωδικοί πρόσβασης κανονικοποιούνται με NFC (ή καθόλου) και συγκρίνονται ως bytes αφού γίνει ακριβώς το ίδιο και στις δύο πλευρές.
  • Μια διάπλαση συμβατότητας μπορεί να μετατρέψει έναν χαρακτήρα σε πολλούς. Ο σύνδεσμος fi είναι ένα σημείο κώδικα που γίνεται δύο γράμματα, οπότε ένα πεδίο που επικυρώθηκε ως προς το μήκος πριν την κανονικοποίηση μπορεί να είναι μακρύτερο μετά. Πρώτα κανονικοποίηση, έπειτα επικύρωση, έπειτα αποθήκευση.
  • Το NFKC δεν αφαιρεί τα πάντα αόρατα. Ένας επιλογέας παραλλαγής επιβιώνει του NFKC και του NFKD, και το ίδιο ένας συνδετικός μηδενικού πλάτους. Γι' αυτό η σελίδα έχει λειτουργία σάρωσης: η κανονικοποίηση απαντά σε ερώτημα για το σχήμα και την ταυτότητα ενός χαρακτήρα, ενώ ο σαρωτής απαντά αν κάτι υπάρχει καθόλου.
Γραφήματα, σημεία κώδικα και bytes

Η κανονικοποίηση αλλάζει τον δεύτερο από αυτούς τους αριθμούς και συνήθως αφήνει τον πρώτο ως έχει. Και οι τρεις αριθμοί είναι «το μήκος του κειμένου» και όλοι είναι διαφορετικοί, γι' αυτό ένα πεδίο φόρμας με όριο 20 χαρακτήρων μπορεί να αντιστοιχεί σε συμβολοσειρά που είναι 20 σημεία κώδικα, 30 μονάδες UTF-16 και 47 bytes.

Ο αριθμός στη στήλη των γραφημάτων είναι αυτός που μετρά ένας άνθρωπος. Το κενό ανάμεσα σε αυτόν και το πλήθος των σημείων κώδικα είναι ακριβώς το αντικείμενο του σαρωτή: ένα όνομα από 12 σημεία κώδικα μπορεί να εμφανιστεί ως 12 γράμματα, ως 3 γράμματα με 9 αόρατα σημάδια ανάμεσά τους, ή ως ένα emoji.

Οι χαρακτήρες που δεν βλέπει κανείς

Ένα κενό μηδενικού πλάτους δεν καταλαμβάνει πλάτος, δεν αλλάζει γλυφή και είναι πραγματικός χαρακτήρας με πραγματικό σημείο κώδικα. Δύο αναγνωριστικά που φαίνονται πανομοιότυπα στην οθόνη μπορεί να διαφέρουν κατά έναν τέτοιο χαρακτήρα, και έτσι ένα όνομα σε λίστα μπορεί να είναι ψευδές αντίγραφο που ταξινομείται, αναζητείται και πιστοποιείται διαφορετικά από το όνομα δίπλα του. Το ίδιο ισχύει για τους αμφίδρομους ελέγχους, που μπορούν να κάνουν μια συμβολοσειρά να εμφανιστεί με σειρά διαφορετική από αυτήν με την οποία γράφτηκε — τέχνασμα που έχει χρησιμοποιηθεί για να αλλοιωθούν ονόματα αρχείων.

Ο σαρωτής της τρίτης λειτουργίας τους απαριθμεί όλους με τη θέση τους: χαρακτήρες μηδενικού πλάτους και συνδέσμων, αμφίδρομους ελέγχους, επιλογείς παραλλαγής, ασυνήθιστα κενά, χαρακτήρες ελέγχου C0 και C1, σημεία κώδικα ιδιωτικής χρήσης, και ένα ψευδο-χαρακτήρα που δεν είναι μισό έγκυρου ζεύγους, πράγμα που δεν είναι καν χαρακτήρας. Ο πίνακας κατηγοριών παραπάνω στη σελίδα λέει τι κάνει κάθε κατηγορία, και το κουμπί αφαίρεσης αφαιρεί ένα αντίγραφο αυτών των χαρακτήρων χωρίς να αγγίζει την είσοδο.

Τα λάθη που σχεδιάστηκε αυτή η σελίδα να εντοπίζει
  • Σύγκριση δύο συμβολοσειρών χωρίς κανονικοποίηση. Το 'é' === 'é' είναι ψευδές. Κανονικοποιούνται και οι δύο πλευρές με την ίδια μορφή πριν τη σύγκριση, και χρησιμοποιείται η ίδια μορφή στο ευρετήριο με το οποίο γίνεται η σύγκριση.
  • Κανονικοποίηση μόνο από τη μία πλευρά. Αναζήτηση που κανονικοποιεί το ερώτημα αλλά όχι την αποθηκευμένη τιμή δεν βρίσκει τίποτα, και μοιάζει με πρόβλημα δεδομένων αντί για πρόβλημα κανονικοποίησης.
  • Μορφοποίηση πεδίου με μορφή συμβατότητας και χαρακτηρισμός του ως συμβατό με NFC. Το NFKC που εφαρμόζεται σε κωδικό πρόσβασης μειώνει τον χώρο των πιθανών κωδικών και συγχωνεύει εισόδους που έπρεπε να είναι διαφορετικές.
  • Η παραδοχή ότι μια μορφή κανονικοποίησης διορθώνει μια απάτη. Δεν αφαιρεί έναν συνδετικό μηδενικού πλάτους, έναν επιλογέα παραλλαγής ή ένα ομοιόγραφο — ένα κυριλλικό а και ένα λατινικό a είναι διαφορετικά σημεία κώδικα που το NFC δεν αγγίζει.
  • Εμπιστοσύνη σε ένα πλήθος χαρακτήρων. Το πλήθος που βλέπει ο χρήστης, το πλήθος που επιβάλλει η στήλη της βάσης δεδομένων και το πλήθος που επιβάλλει ένα όριο bytes είναι τρεις διαφορετικοί αριθμοί, και οι γραμμές παραπάνω δείχνουν πόσο μπορεί να διαφέρουν.
  • Η παραδοχή ότι το NFC είναι αυτό που θα λάβει κανείς. Το κείμενο που έρχεται από μέθοδο εισαγωγής, αρχείο ή άλλο σύστημα βρίσκεται στη μορφή που παρήγαγε εκείνο το σύστημα. Η σελίδα ξεκινά με NFC επειδή αυτή είναι η μορφή που αναμένεται να χρησιμοποιεί ο ιστός, όχι επειδή αυτό είναι το αποτέλεσμα της εισόδου.
Σύντομες απαντήσεις

“Γιατί δύο συμβολοσειρές που φαίνονται ίδιες δεν είναι ίσες;”

Επειδή ο ένας γράφεται με σύνθετο χαρακτήρα και ο άλλος με βασικό γράμμα συν συνδυαστικό σημάδι, ή επειδή ο ένας περιέχει χαρακτήρα που δεν σχεδιάζει τίποτα. Μετάβαση στη λειτουργία Σύγκρισης και τοποθέτηση μιας συμβολοσειράς σε κάθε πλαίσιο: η σελίδα αναφέρει την πρώτη θέση στην οποία διαφέρουν οι κανονικοποιημένες συμβολοσειρές και τι βρίσκεται σε αυτή τη θέση σε καθεμία.

“Ποια μορφή πρέπει να χρησιμοποιώ σε μια εφαρμογή;”

NFC για ό,τι θα διαβάσει, θα εμφανίσει, θα αποθηκεύσει και θα αναζητήσει ένας άνθρωπος, που είναι η μορφή που συνιστά το W3C για τον ιστό. Το NFD είναι η σωστή επιλογή όταν ο κώδικας από πίσω θέλει βασικά γράμματα και χωριστά σημάδια, κάτι συνηθισμένο στην επεξεργασία κειμένου και σε ορισμένα συστήματα αρχείων στο macOS. Το NFKC ή NFKD χρησιμοποιούνται μόνο όπου γίνεται αντιστοίχιση και όχι αποθήκευση: ευρετήρια αναζήτησης, φίλτρα λέξεων-κλειδιών, εντοπισμός διπλοτύπων.

“Παραμένει κανονικοποιημένη μια συμβολοσειρά που ήδη είναι κανονικοποιημένη αφού τη συνενώσω με κάτι άλλο;”

Όχι απαραίτητα. Η κανονικοποίηση δεν διατηρείται με τη συνένωση: η ένωση δύο συμβολοσειρών NFC μπορεί να παράγει ακολουθία που το NFC θα είχε γράψει διαφορετικά, επειδή ένα συνδυαστικό σημάδι στην αρχή της δεύτερης συμβολοσειράς μπορεί να προσκολληθεί στον χαρακτήρα στο τέλος της πρώτης. Κανονικοποίηση μετά τη συναρμολόγηση, και όχι μόνο πριν από αυτήν.

“Αφαιρεί η κανονικοποίηση τους αόρατους χαρακτήρες;”

Όχι, με μία μερική εξαίρεση: το NFKC και το NFKD αντικαθιστούν τον κενό χαρακτήρα χωρίς διακοπή με συνηθισμένο κενό, που είναι ορατό αποτέλεσμα της σύμπτυξης συμβατότητας. Όλα τα υπόλοιπα — κενά μηδενικού πλάτους, συνδέσμοι, αμφίδρομοι έλεγχοι, επιλογείς παραλλαγής — περνούν και από τις τέσσερις μορφές αναλλοίωτα. Γι' αυτό υπάρχει η τρίτη λειτουργία.

“Γιατί αλλάζει το πλήθος bytes όταν δεν αλλάζει το πλήθος χαρακτήρων;”

Επειδή οι δύο μορφές χτίζονται από διαφορετικά σημεία κώδικα που κοστίζουν διαφορετικό αριθμό bytes σε UTF-8. Ένα προσυντεθειμένο é είναι δύο bytes· το αποσυντεθειμένο ζεύγος είναι τρία. Ο πίνακας καταμέτρησης παραπάνω εκτυπώνει και τους τέσσερις αριθμούς δίπλα-δίπλα για την είσοδο και για κάθε μορφή, ώστε η διαφορά να φαίνεται και όχι απλώς να περιγράφεται.

“Πώς ξέρω ότι τα αποτελέσματα αυτής της σελίδας είναι σωστά;”

Με το Αυτοτέστ κανονικοποιείται κάθε περίπτωση του αρχείου δεδομένων της σελίδας, ελέγχει τις τέσσερις μορφές μεταξύ τους για τις τεκμηριωμένες σχέσεις (το NFC είναι ιδεμποτέντ, το NFD ενός συμβολοσειράς NFC είναι το ίδιο με το NFD της αρχικής, και ούτω καθεξής), περνά κάθε χαρακτήρα που αναφέρουν οι πίνακες αόρατων και ελέγχου από τον σαρωτή, και μετρά τις περιπτώσεις γραφημάτων έναντι του Intl.Segmenter.

Πρότυπα και αναφορές

Οι τέσσερις μορφές και οι ορισμοί τους προέρχονται από το Unicode Standard, Annex 15 (Unicode Normalization Forms). Τα σημεία κώδικα που χρησιμοποιούνται ως παραδείγματα αποσύνθεσης είναι αυτά που ορίζει το πρότυπο· η σελίδα δεν υλοποιεί η ίδια τους πίνακες αποσύνθεσης — καλεί το δικό του String.prototype.normalize του προγράμματος περιήγησης, δηλαδή την ίδια υλοποίηση που χρησιμοποιεί η πλατφόρμα για είσοδο κειμένου, ταξινόμηση και σύγκριση, οπότε αυτό που αναφέρεται εδώ είναι αυτό που κάνει το υπόλοιπο σύστημα με την ίδια συμβολοσειρά. Τα συμπλέγματα γραφημάτων προέρχονται από το Intl.Segmenter όπου το παρέχει το πρόγραμμα περιήγησης, και από τεκμηριωμένη προσέγγιση όπου δεν το παρέχει.

Το κείμενο της σελίδας γράφτηκε για αυτόν τον ιστότοπο. Κάθε παράδειγμα της σελίδας παράχθηκε από τον δικό της κώδικα και ελέγχθηκε έναντι της καταγεγραμμένης λίστας περιπτώσεων του αρχείου δεδομένων, που με τη σειρά της ελέγχθηκε έναντι των δεδομένων NormalizationTest του Unicode για τους ίδιους χαρακτήρες. Οι πίνακες αόρατων και επικίνδυνων χαρακτήρων είναι επιλεγμένο υποσύνολο γραμμένο για προειδοποιήσεις αναγνώσιμες από ανθρώπους, όχι το πλήρες προφίλ ασφαλείας του Unicode (UTS #39), και η σελίδα το λέει αντί να υπονοεί πληρότητα που δεν έχει.