Περίληψη
Ένα std::string αποθηκεύει byte, όχι γράμματα, και στο σημερινό κείμενο ένα γράμμα μπορεί να πιάνει από ένα έως τέσσερα byte. Αυτό το μάθημα διακρίνει τρία πράγματα που ο καθημερινός λόγος συγχέει: τον αφηρημένο χαρακτήρα, που το Unicode τον ταυτίζει με έναν αριθμό, το σημείο κώδικα· τα byte με τα οποία τον κωδικοποιεί το UTF-8· και τη μορφή που σχεδιάζει η οθόνη. Εξηγεί πώς το UTF-8 μοιράζει τα bit ενός σημείου κώδικα στα byte του, γιατί η λέξη «έτος» πιάνει οκτώ byte και πώς μετριούνται οι χαρακτήρες μιας συμβολοσειράς χωρίς αποκωδικοποίηση. Δείχνει έπειτα ποιες πράξεις χαλούν το κείμενο (αντιστροφή byte προς byte, μετατροπή σε κεφαλαία byte προς byte, κόψιμο στη μέση ενός χαρακτήρα), ποιες λειτουργούν χωρίς καμία προφύλαξη (αποθήκευση, αντιγραφή, συνένωση, αναζήτηση, εγγραφή) και τι δεν κάνει η πρότυπη βιβλιοθήκη της C++23: κεφαλαία έξω από το ASCII, αλφαβητική σειρά κατά γλώσσα, κανονικοποίηση.
Μαθησιακοί στόχοι
- Να εξηγείτε πώς το UTF-8 κωδικοποιεί ένα σημείο κώδικα σε ένα έως τέσσερα byte και να αποκωδικοποιείτε με το χέρι τα byte του ισπανικού γράμματος «ñ».
- Να προβλέπετε το μέγεθος σε byte μιας συμβολοσειράς με γράμματα εκτός ASCII, όπως η
"έτος"(8) ή η"πρόγραμμα"(18). - Να μετράτε τους χαρακτήρες μιας συμβολοσειράς UTF-8 μετρώντας τα byte που δεν είναι byte συνέχειας, με τη συνθήκη
(c & 0xC0) != 0x80. - Να αναγνωρίζετε τις πράξεις που χαλούν το κείμενο UTF-8 (αντιστροφή byte προς byte,
std::toupperανά byte, κόψιμο στη μέση ενός χαρακτήρα) και να αποφασίζετε τι θα κάνετε σε κάθε περίπτωση.
Χαρακτήρες, σημεία κώδικα και byte
Τρία επίπεδα
Το μάθημα Χαρακτήρες, λογικές τιμές, μετατροπές και σταθερές παρουσίασε τον char ως ακέραιο ενός byte που γράφεται ως γράμμα σύμφωνα με τον πίνακα ASCII και προειδοποίησε ότι το 'α' δεν χωράει σε αυτόν· το μάθημα Μεταγλώττιση, εκτέλεση και ρύθμιση έδειξε ότι η "έτος" πιάνει οκτώ byte και προετοίμασε το τερματικό για να τη γράφει. Αυτό το μάθημα εξηγεί το γιατί. Το ASCII αντιστοιχίζει έναν αριθμό σε 128 χαρακτήρες, από το 0 έως το 127, που χωρούν σε επτά bit: τα γράμματα της αγγλικής, τα ψηφία, τα σημεία στίξης και μερικούς κωδικούς ελέγχου. Ωστόσο, το πραγματικό κείμενο περιέχει ελληνικά γράμματα με τόνους και διαλυτικά, κυριλλικά, κινεζικά ιδεογράμματα, μαθηματικά σύμβολα και emoji, που φτάνουν τις εκατοντάδες χιλιάδες. Από αυτό έπεται ότι χρειάζεται, πρώτον, ένας πολύ μεγαλύτερος πίνακας και, δεύτερον, ένας τρόπος να γράφονται οι αριθμοί του σε byte.
Αξίζει να διακρίνουμε τρία επίπεδα. Το πρώτο είναι ο αφηρημένος χαρακτήρας: το ισπανικό γράμμα «πεζό ενιέ», ανεξάρτητα από το πώς αποθηκεύεται ή σχεδιάζεται. Το Unicode είναι το πρότυπο που αντιστοιχίζει σε κάθε αφηρημένο χαρακτήρα έναν αριθμό, το σημείο κώδικα του, που γράφεται U+ ακολουθούμενο από τουλάχιστον τέσσερα δεκαεξαδικά ψηφία: το «ñ» είναι το U+00F1 (241), το «A» είναι το U+0041 (65, ο ίδιος αριθμός όπως στο ASCII), το «€» είναι το U+20AC (8364). Τα σημεία κώδικα πηγαίνουν από το 0 έως το U+10FFFF, λίγο περισσότερα από ένα εκατομμύριο. Το δεύτερο επίπεδο είναι η κωδικοποίηση: ο κανόνας που μετατρέπει κάθε σημείο κώδικα σε μια ακολουθία byte. Το UTF-8 είναι η κωδικοποίηση που χρησιμοποιούν ο ιστός, τα σημερινά συστήματα και αυτός ο κύκλος μαθημάτων. Το τρίτο επίπεδο είναι η απεικόνιση: η μορφή που σχεδιάζει η γραμματοσειρά στην οθόνη, η οποία μπορεί να αλλάζει από μια γραμματοσειρά σε άλλη χωρίς να αλλάζει ο χαρακτήρας.
Αν είναι έτσι, ένα κειμενικό δεδομένο δεν είναι κείμενο όσο δεν είναι γνωστή η κωδικοποίησή του: τα byte c3 b1 είναι το «ñ» σε UTF-8, αλλά, αν διαβαστούν με τον πίνακα ενός παλιού τερματικού, είναι δύο σύμβολα άσχετα με αυτό, που είναι η βλάβη που το μάθημα 1.4 δίδαξε να διαγιγνώσκουμε. Το ίδιο ζήτημα, ιδωμένο από τη στοιχειοθεσία εγγράφων, εμφανίζεται στο μάθημα «Γλώσσες και γραμματοσειρές» του κύκλου μαθημάτων LaTeX για την ακαδημαϊκή γραφή.
Βλέποντας τα byte
Για να δούμε τα byte μιας συμβολοσειράς αρκεί να τη διατρέξουμε: ένα std::string και ένα std::string_view είναι ακολουθίες από char, και ο βρόχος for με εύρος δίνει ένα ένα τα byte τους. Για να τα γράψουμε στο δεκαεξαδικό χρησιμεύει το πεδίο {:02x} (δύο δεκαεξαδικά ψηφία, με μηδενικό στα αριστερά). Όπως με το {:d} στο μάθημα 2.4, και λόγω της ίδιας διόρθωσης του προτύπου που υιοθετήθηκε το 2023 και εφαρμόζεται από τον GCC 15, το {:02x} γράφει έναν char ως byte χωρίς πρόσημο. Ο κανόνας μετράει, γιατί ο char έχει πρόσημο στον GCC σε x86-64: το byte c3, μετατρεπόμενο σε int, είναι ο αριθμός , που το {:02x} θα έγραφε -3d.
#include <print>#include <string_view>// Τα byte ενός κειμένου.// Προσυνθήκη: καμία.// Μετασυνθήκη: γράφει μια γραμμή με το text, το μέγεθός του σε byte και κάθε byte στο δεκαεξαδικό.void printBytes(std::string_view text){ std::print("{:<4}{}:", text, text.size()); for (const char c : text) { std::print(" {:02x}", c); } std::println("");}int main(){ printBytes("A"); printBytes("ñ"); printBytes("€"); printBytes("😀"); printBytes("έτος");}A 1: 41ñ 2: c3 b1€ 3: e2 82 ac😀 4: f0 9f 98 80έτος8: ce ad cf 84 ce bf cf 82Κάθε γραμμή δείχνει το κείμενο, το μέγεθός του και τα byte του. Ένας χαρακτήρας από κάθε μήκος: το «A» πιάνει ένα byte, το «ñ» δύο, το «€» τρία και το emoji τέσσερα. Η λέξη «έτος» πιάνει οκτώ byte, ce ad, cf 84, ce bf και cf 82, και το size() της είναι 8, όχι 4. Παρατηρήστε ότι το «A» είναι το byte 41, το ίδιο όπως στο ASCII: το UTF-8 γράφει τους 128 χαρακτήρες του ASCII ακριβώς όπως το ASCII.
Το σχήμα ακολουθεί την κωδικοποίηση του «ñ» από πάνω προς τα κάτω: το σημείο κώδικα, τα bit του, το μοίρασμα αυτών των bit και τα δύο byte που προκύπτουν, με τα bit του προθέματος τονισμένα.
Απόδειξη
Το «ñ» είναι το σημείο κώδικα
U+00F1, δηλαδή .Στο δυαδικό, το γράφεται
11110001. Είναι μεγαλύτερο από 127 και μικρότερο από 2048, οπότε το UTF-8 το γράφει σε δύο byte, που μεταφέρουν έντεκα bit: συμπληρώνεται με μηδενικά στα αριστερά ως τα έντεκα,00011110001.Τα έντεκα bit μοιράζονται σε δύο ομάδες: τα πέντε πρώτα,
00011, και τα έξι τελευταία,110001.Το πρώτο byte φέρει το πρόθεμα
110, που αναγγέλλει χαρακτήρα δύο byte, και ακολουθούν τα πέντε bit της πρώτης ομάδας:11000011. Το δεύτερο φέρει το πρόθεμα10, που σημαδεύει ένα byte συνέχειας, και ακολουθούν τα έξι υπόλοιπα bit:10110001.Στο δεκαεξαδικό, το
1100 0011είναιc3και το1011 0001είναιb1: τα δύο byte που έγραψε για το «ñ» το προηγούμενο πρόγραμμα. Η αλλαγή βάσης ανάμεσα στο δυαδικό και στο δεκαεξαδικό, ανά τέσσερα bit, είναι αυτή του μαθήματος «Θεσιακά συστήματα αρίθμησης» του κύκλου μαθημάτων Άλγεβρα και γεωμετρία I.
Φόρτωση του περιεχομένου…
Ολοκληρώθηκε
Συνδεθείτε για να αποθηκεύσετε την πρόοδό σας