Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

Γειά σας, θα ήθελα αν ξέρει κάποιος να με βοηθήσει να χρησιμοποιήσω unicode/utf-8 χαρακτήρες σε ένα πρόγραμμα γραμμένο σε C++.. Το περιβάλλον μου είναι το visual (studio) C++ 2010. Eνδιαφέρομαι αποκλειστικά για Windows, console application για την ώρα..

Θα ήθελα αν γίνεται να μη βασίζεται το πρόγραμμα στις τοπικές ρυθμίσεις του συστήματος, αλλά να λειτουργεί αυτόνομα σε όλα τα συστήματα..

Ένα παράδειγμα θα ήταν ευχάριστο, αλλά και λίγη βασική θεωρία θα ήταν εξαιρετική.

Ευχαριστώ :)

Δημοσ. (επεξεργασμένο)

Όσον αφορά τη λύση απλά δες εδώ. Συνοπτικά, για να τυπώνεις συγκεκριμένα UTF-8:

  • Αν θέλεις να χρησιμοποιήσεις wprintf (γιατί?) τότε πρέπει να έχεις δώσει SetConsoleOutputCP(CP_UTF8). Αν τα strings που θα τυπώσεις όμως είναι wide character string literals (L"παράδειγμα") σωσμένα μέσα στο source τότε πρέπει να κάνεις πρώτα το κατάλληλο encoding conversion σε UTF-8 με τη WideCharToMultiByte. Παράδειγμα υπάρχει στην ερώτηση (αλλά δεν κάνει delete[] τον buffer, εσύ μη το ξεχάσεις).
  • Αν θέλεις να χρησιμοποιήσεις wcout τότε πρέπει να έχεις δώσει _setmode(_fileno(stdout), _O_U8TEXT). Το ίδιο που είπα για τα string literals παραπάνω ισχύει και εδώ. Παράδειγμα στην πρώτη απάντηση (όμως εκεί το κάνει λίγο διαφορετικά, βάζει το mode σε UTF-16 και τυπώνει wide string literal κατευθείαν).

Το θέμα με τα character sets και τα encodings είναι τεράστιο και έχει πολλά λεπτά σημεία από μόνο του (πόσο μάλλον όταν μπαίνουν στην εξίσωση και παράμετροι που έχουν να κάνουν καθαρά με την υλοποίηση του εκάστοτε C runtime όπως εδώ). Τέλος πάντων ορίστε ένα crash course, αλλά θα πρέπει να το ψάξεις και μόνος σου.

 

Τα απολύτως βασικά περί Unicode

 

Για να μιλάμε για κείμενο πρέπει να ορίσουμε πρώτα ένα σύνολο από χαρακτήρες (character set). Το κείμενο είναι μια πεπερασμένου μήκους ακολουθία στοιχείων αυτού του συνόλου. Στους χαρακτήρες με την κοινή έννοια του όρου (δηλαδή γράμματα, νούμερα κλπ) προσθέτουμε και άλλους που κανονικά "δεν στέκουν μόνοι τους" (π.χ. ένας τόνος μόνος του) καθώς και άλλους που δεν είναι καν "εκτυπώσιμοι" (π.χ. ο χαρακτήρας "αλλαγή γραμμής). Όλοι αυτοί οι χαρακτήρες μαζί ονομάζονται abstract characters και το σύνολό τους απαρτίζει το character set.

 

Κάθε εκτυπώσιμος χαρακτήρας έχει μια οπτική αναπαράσταση η οποία ονομάζεται glyph. Όλοι οι χαρακτήρες (ανεξαιρέτως) αντιστοιχίζονται ένα προς ένα με code points (βασικότατη έννοια!), τα οποία είναι νούμερα που τους έχουμε αποδώσει για να τους ξεχωρίζουμε εύκολα. Στο ASCII για παράδειγμα μπορείς κατ' αντιστοιχία να πεις ότι ο character "ινδο-αραβικό ψηφίο μηδέν" έχει code point 48 (το γνωστό ως ASCII code) και αναπαρίσταται με το glyph "0". Στο Unicode όλοι οι χαρακτήρες έχουν ένα official όνομα και τα code points αναφέρονται με τη μορφή U+xxxx. Το σύνολο (με τη μαθηματική έννοια) των αριθμών που είναι code points ονομάζεται και codespace.

 

Το encoding είναι ο μηχανισμός με τον οποίο μια σειρά από code points αναπαρίσταται σε μορφή bytes. To Unicode δεν είναι encoding, είναι ένα διεθνές στάνταρ το οποίο ορίζει και ένα character set και διάφορα encodings τα οποία μπορούν να χρησιμοποιηθούν για την μετάδοση κειμένων που αποτελούνται από χαρακτήρες αυτού του character set. Τέτοια encodings είναι το UTF-8 και άλλα.

 

Παύση βομβαρδισμού: σ' αυτή τη φάση θα πρέπει να ξέρεις αρκετά για να καταλάβεις τα βασικότερα απ' όσα λέει αυτή η σελίδα.

 

Όλα ανεξαιρέτως τα encodings που ορίζονται σαν μέρος του Unicode μπορούν να χρησιμοποιηθούν για τη μετάδοση οποιουδήποτε Unicode κειμένου. Η μορφή του κειμένου όμως "on the wire" (δηλαδή ο αριθμός και οι τιμές των bytes που το αναπαριστούν) αλλάζουν από encoding σε encoding ενώ το κείμενο (όπως το αντιλαμβάνεται ένας άνθρωπος) παραμένει το ίδιο.

 

Τα λιγότερο απολύτως βασικά

 

Κάθε χαρακτήρας ανήκει σε μία character general category η οποία χαρακτηρίζει τον τύπο του. Επίσης κάθε χαρακτήρας ανήκει σε ένα block (για να ομαδοποιούμε τους χαρακτήρες που ανήκουν στο ίδιο σύστημα γραφής, π.χ. υπάρχει το block με τα ελληνικά). Τα blocks με τη σειρά τους ομαδοποιούνται σε planes. Το βασικότερο plane (και το μοναδικό με το οποίο θα ασχοληθείς ποτέ στην πράξη) είναι το Basic Multilingual Plane (BMP).

 

Πάμε για κάτι πιο tricky... υπάρχουν glyphs τα οποία προκύπτουν από πολλές διαφορετικές σειρές χαρακτήρων. Τι είπα τώρα?

 

Δες τη σελίδα που αναφέρεται στο ελληνικό κεφαλαίο Α με τόνο. Τι μας λέει; Ότι στο Unicode υπάρχει το code point U+0386 το οποίο αντιστοιχεί απευθείας στο χαρακτήρα αυτό. Όμως... δες κι εκεί που λέει "decomposition". Στο Unicode υπάρχει επίσης το κεφαλαίο ελληνικό Α χωρίς τόνο (U+0391) αλλά και ο χαρακτήρας "COMBINING ACUTE ACCENT" (U+0301). Ο τελευταίος, παρόλο που υπάρχει σαν glyph για να μπορούμε να δούμε με τι μοιάζει, δεν στέκει από μόνος του σε κείμενο. Αυτό που συμβαίνει είναι ότι "προσθέτει τόνο" στον χαρακτήρα που προηγείται. Επομένως, τα "κέιμενα"

  1. U+0386
     
  2. U+0391 U+0301

είναι δύο διαφορετικά κείμενα όσον αφορά τον υπολογιστή, αλλά το "ίδιο" κείμενο όσον αφορά έναν άνθρωπο που θα διαβάσει την τυπωμένη τους μορφή. Αυτό σημαίνει πως αν πρόκειται να συγκρίνουμε δύο ίδια από άποψης glyphs Unicode κείμενα θα πρέπει να βρίσκονται στην ίδια "μορφή αναπαράστασης", η οποία ονομάζεται normalization form (κατ' αναλογία με το ότι για να συγκρίνουμε δύο ίδια από άποψης code point κείμενα byte προς byte, θα πρέπει αυτά να έχουν αποθηκευτεί με το ίδιο encoding).

Επεξ/σία από defacer
  • Like 2
Δημοσ.

Περα απο αυτα που λεει ο defecer.

 

Το μπερδεμα ειναι οτι πολλοι χαρακτηριζουν το

>
char *str = "hello";

ως string ή μια σειρα απο χαρακτηρες. Για την ακριβεια το παραπανω ειναι stream ή μια σειρα απο bits.

 

 

>
std::string str = "Hello";
std::cout<< str;

Το παραπανω θα σου βγαλει στην console hello και θα θεωρησεις οτι ολα ειναι σωστα. Ομως δεν ειναι σωστα. Βγαζεις σωστο αποτελεσμα επειδη υπαρχει ταυτιση στο range 0-127 αναμεσα σε ολα τα codepages.

Δηλαδη εχεις το codepage της console x και codepage στο str y, επειδη ομως το hello ειναι λατινικοι χαρακτηρες αυτοι ανηκουν στο 0-127 range αρα x[0,127] = y[0,127].

 

Το οτι τα πραγματα τελικα ειναι λαθος το καταλαβαινεις οταν ξεπερασεις το range 0-127

 

Ενα απλο παραδειγμα.

Γιατι αυτο δουλευει με Ελληνικους χαρακτηρες

>
int main(int argc, char **argv)
{
std::string input;
std::cin
 >> input;
std::cout
 << std::endl
 << input;

return 0;
}

 

και αυτο δεν δουλευει

 

>
int main(int argc, char **argv)
{
std::string input = "Γεια";
/*
std::cin
 >> input;
 */
std::cout
 << std::endl
 << input;

return 0;
}

 

Γιατι δουλευει το πρωτο; Πολυ απλα το stream (string) το παιρνεις απο συγκεκριμενο stream reader/writer (console) με αποτελεσμα το input να ειναι απλα ενας buffer για αοριστο stream (ή καλυτερα για string χωρις προσδιορισμενο encoding)

 

Αρα εχεις console ---> stream με x encoding ---> input (ενας buffer) ---> stream με x encoding --->console

Εφοσον εχεις ενα ορισμενο encoding τα παντα δουλευουν

 

 

Στο δευτερο παραδειγμα εχεις δυο encoding

Ενα της console λεμε x

Και αλλο ενα του input (file encoding) λεμε y

 

Αν εχεις windows με Ελληνικα, αυτα τα δυο δεν υπαρχει περιπτωση να συμπιπτουν με αποτελεσμα να βλεπεις κορακια αντι για "Γεια"

 

Η λυση ειναι πολυ απλη. Πριν αρχισεις το προτζεκτ, επιλεγεις με τι encoding θα δουλεψεις και πριν κανεις compile θα αλλαξεις το encoding του project σου, επειτα σε μαι συναρτηση πχ initMyApp θα σεταρεις το encoding του stream reader/writer (console)

 

Βεβαια μπορεις να σεταρεις το project σου σε greek dos με αποτελεσμα το προγραμμα να δουλευει ρολοι σε windows με ελληνικο local (καλυτερα ομως, σεταρε και την console ωστε να εισαι 100% σιγουρος οτι ολα θα δουλεψουν)

 

 

Η λυση του wide char δυστυχος σε console δεν μας κανει (πρεπει να σεταρεις τα glyphs αλλη ιστορια)

 

Τωρα για για utf8-16-32 etc.. θα τα κανεις ολα convert στο encoding που εχεις επιλεξει για το προγραμμα σου.

 

Ολη η ιστορια ειναι η επιλογη ενος encoding με το οποιο θα δουλευει το προγραμμα σου, ολα τα περιφερειακα θα πρεπει να περνανε απο εναν converter επειδη θεωρουνται stream (data/bits απροσδιοριστα δεδομενα ρε παιδι μου)

 

Δυστυχος λαθη με encoding υπαρχουν αποιρα στη ζωη μας, subtitles, sites etc ολα αυτα επειδη καποιος δεν ειπε στον stream reader τι τον ταιζει.

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...