migf1 Δημοσ. 10 Φεβρουαρίου 2012 Δημοσ. 10 Φεβρουαρίου 2012 (επεξεργασμένο) Μετά από αρκετό ψάξιμο, κατάφερα να στέλνω πραγματική Unicode έξοδο στην κονσόλα των Windows, ανεξαρτήτως τρέχουσας κωδικοσελίδας, σε σκέτη C. Δεν λειτουργεί όμως όταν ή έξοδος είναι redirected (θέλει κι άλλο ψάξιμο αυτό, και νομίζω πως θέλει και αρκετή δουλειά) . Η γενική ιδέα είναι πως από τα XP και μετά, τα πάντα στα Windows λειτουργούν εσωτερικά σε UTF-16LE (Unicode 16-bit Little Endian) οπότε οτιδήποτε άλλο (όπως οι OEM και ANSI κωδικοσελίδες της κονσόλας) μεταφράζονται έτσι κι αλλιώς σε Unicode. Οπότε, δουλεύοντας εξαρχής με wchar_t (που στα Windows ισοδυναμεί με 16-bits) μπορούμε να χρησιμοποιήσουμε απευθείας την ρουτίνα WriteConsoleW() του Win32, η οποία αναλαμβάνει να μετατρέψει το wide-string σε UTF-16LE και να το τυπώσει στην κονσόλα Τα Windows έχουν συγκεκριμένα abstractions με τα οποία μέσα από συγκεκριμένες διαδικασίες μπορεί κανείς να γράψει κώδικα που να δουλεύει είτε σε ANSI είτε σε Unicode mode, αλλά όχι και στα 2 ταυτόχρονα. Οι διαδικασίες αυτές μεταξύ άλλων προϋποθέτουν... α) επιλογή για χρήση ή όχι των pre-processor constants _UNICODE (κονσόλα) και UNICODE (gui) β) τη χρήση του header αρχείου <tchar.h>, γ) τη χρήση του τύπου TCHAR (που ανάλογα με το α) μεταφράζεται είτε σε char είτε σε wchar_t δ) τη χρήση του macro TEXT() ή _TEXT() σε όλα τα string literals του κώδικα ε) τη χρήση generic συναρτήσεων διαχείρισης strings, που ορίζονται στο <tchar.h> και ξεκινάνε με _tcs, αντί των στάνταρ str και wcs της στάνταρ βιβλιοθήκης, και δουλεύουν με τον τύπο TCHAR. Ο κώδικας που έγραψα εγώ δεν χρησιμοποιεί τίποτα από τα παραπάνω και δουλεύει κανονικά με executables παραγόμενα από migw32 gcc και pelles-c 32/64, τόσο σε Windows XP 32bits όσο και σε Windows 7 64bits (σε Vista δεν έχω πρόσβαση). Χρειάζομαι όμως επιβεβαίωση από κάποιον που έχει εγκατεστημένο το Visual Studio ή την VC++ ότι γίνονται κανονικά compile κι εκεί, και κυρίως ότι δουλεύει σωστά το εκτελέσιμο. Η μόνη προϋπόθεση είναι να έχετε την κονσόλα γυρισμένη σε γραμματοσειρά ικανή να απεικονίσει Unicode χαρακτήρες (Lucida Console στα XP, Consolas στα 7άρια... η Consolas απεικονίζει περισσότερους Unicode χαρακτήρες από ότι η Lucida Console). Η κωδικοσελίδα μπορεί να είναι οποιαδήποτε, ακόμα και OEM ! Παρόλο που κι ο δικός μου κώδικας λειτουργεί μονάχα σε Windows (XP ή μεταγενέστερα) χρησιμοποιεί μονάχα μια Windows specific συνάρτηση, την: WriteConsoleW() που είναι η wide-string υλοποίηση της abstract: WriteConsole() (αυτή η τελευταία είναι abstraction των Windows που καλεί είτε την WriteConsoleW() όταν είναι ορισμένη η σταθερά _UNICODE, είτε την WriteConsoleA() όταν δεν είναι ορισμένη η σταθερά... τα W και Α δηλώνουν wide και ansi (multibyte) strings, αντίστοιχα). Χρησιμοποιώ επίσης κάποιους τύπους των Windows (DWORD, UINT, HANDLE, κλπ) γιατί βαριόμουν να κάτσω να τα αντιστοιχίσω με exact types από το <stdint.h> της C99 Για να μπορείτε να γράφετε απευθείας μέσα στον κώδικα Unicode τιμές των string literals (όπως κάνω εγώ) θα πρέπει να δουλεύετε το source αρχείο .c σε Unicode κωδικοποίηση... UTF-8 ή UTF-16, ότι παρέχει ο text editor/IDE σας. Ιδανικά με BOM, γιατί χωρίς BOM οι windows oriented compilers μπερδεύονται και τα θεωρούν ANSI). Μπορείτε να χρησιμοποιήσετε και ANSI κωδικοποίηση στα πηγαία σας αρχεία αν το προτιμάτε, με την προϋπόθεση πως οι τιμές που χρησιμοποιείτε στα string-literals υπάρχουν στην συγκεκριμένη ANSI (multi-byte) κωδικοποίηση... αλλιώς θα παραπονεθεί ο compiler. Αν είστε με mingw32 μπορείτε να καθορίσετε την κωδικοποίηση των πηγαίων αρχείων με: -finput-charset=... (για το εκτελέσιμο μπορείτε με: -fexec-charset=... και για τα wide-string με: -fwide-charset-...) αλλά από default θεωρεί Unicode, οπότε προτιμήστε το για τα αρχεία σας Η ουσία είναι πως προσπάθησα να γράψω κώδικα που να είναι code-page agnostic, όσο το δυνατόν συμβατός με τα C99 wide-string στάνταρ της γλώσσας, και να γίνεται compile με τους δημοφιλείς C compiler της πλατφόρμας των Windows. Εκτός από τα Windows specific στοιχεία που περιέγραψα παραπάνω, χρησιμοποιώ και την vswprintf() η οποία δεν ανήκει στα στάνταρ της γλώσσας, και ο κάθε compiler τη χρησιμοποιεί διαφορετικά. Με το #if #elif στην αρχή του κώδικα την έχω κάνει να λειτουργεί σωστά με mingw32 gcc, pelles-c και hopefully με MS Visual Studio/VC++ (βοηθήστε για το Visual Studio όσοι το έχετε please, σε περίπτωση που δεν τρέχει ο κώδικας ή δεν κάνει compile). Τέσσερις συναρτήσεις είναι βασικά ο κώδικας και η main() που τις δοκιμάζει: > DWORD putws_unicode( const wchar_t *wtext ); /* για strings & string literals, σαν την puts() */ DWORD wprint_unicode( const wchar_t *wtext ) /* για strings & string literals, χωρίς να προσθέτει αλλαγή γραμμής στο τέλος */ DWORD wprintf_unicode( const wchar_t *wformat, ... ); /* για formatted output, σαν την wprintf() */ void wprint_unicode_range(const wchar_t *txt, const wchar_t cstart, const wchar_t cend); /* για τύπωμα συνεχόμενων χαρακτήρων από το Unicode table (κατά το ASCII table)*/ Παραθέτω και τον κώδικα (περιμένω με αγωνία νέα από Visual Studio ) > /*****************************************************//** * @file winconsole.c * @version 0.1A * @date 11 Feb, 2012 * @author migf1 <[email protected]> * * @brief Primitive, code-page agnostic, wchar_t wrapper * for Unicode ouput to the Windows console. * * @remark TabSize = 8, Recommended save-file encoding: UTF-8 (with BOM). * * @note Since file streams (including stdin, stdout & stderr) can be either * narrow or wide oriented, a program cannot interact with them in both * ways. This wrapper interacts with the Windows console in a wide * oriented manner (your code should use the wide-string library * when dealing with strings). ********************************************************* */ #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <stdarg.h> #include <wchar.h> /* * Make sure that the non-standard function vswnprintf() is compiled * and works as expected with popular C compilers in the Windows platform. */ #if defined(__MINGW32__) #define vswprintf vsnwprintf #elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(_MSC_FULL_VER) || defined(_MSC_BUILD) #define vswprintf _vsnwprintf #endif /*********************************************************//** * @par Prototype * wint_t putwchar_unicode( const wchar_t wchar ); * * @brief Unicode, code-page agnostic output of a wide-character * to the console ( like a unicode version of putwchar() ). * * @param[in] wchar The wide-character to be outputted. * @return WEOF on error or the character written promoted to wint_t. * * @remark Windows treat internally everything as UTF-16LE. * The function converts its wide argument to UTF-16LE and * outputs it to the console, by using directly the Win32 API. * * @note The console must be using a Unicode enabled font, such * as Lucida Console. The function is NOT working when stdout * is redirected. ************************************************************* */ wint_t putwchar_unicode( const wchar_t wchar ) { DWORD wccount = 0; if ( !WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), &wchar,1, &wccount, NULL) || wccount != 1 ) return WEOF; return (wint_t)wchar; } /*********************************************************//** * @par Prototype * int putws_unicode( const wchar_t *ws ); * * @brief Unicode, code-page agnostic output of a wide-string * and a newline character to the console ( like a Unicode * version of fputws(ws, stdout) plus a newline character ). * * @param[in] ws The nul-terminating wide-string to be outputted. * @return WEOF on error or the number of characters successfully * outputted, neither counting the newline character, nor * the nul-terminating character. * * @remark Windows treat internally everything as UTF-16LE. * The function converts its wide argument to UTF-16LE and * outputs it to the console, by using directly the Win32 API. * * @note The console must be using a Unicode enabled font, such * as Lucida Console. The function is NOT working when stdout * is redirected. * * @sa printws_unicode() ************************************************************* */ int putws_unicode( const wchar_t *ws ) { DWORD ret = 0; /* # of characters written */ /* sanity checks */ if ( !ws || !(*ws)) return WEOF; if ( !WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ws, wcslen(ws), &ret,NULL) || WEOF == putwchar_unicode(L'\n') ) return WEOF; return (int)ret; } /*********************************************************//** * @par Prototype * int printws_unicode( const wchar_t *ws ); * * @brief Unicode, code-page agnostic output of a wide-string * to the console * ( like a Unicode version of fputws(ws, stdout) ). * * @param[in] ws The nul-terminating wide-string to be ouputted. * @return WEOF on error, or the number of characters successfully * outputted, not counting the nul-terminating character. * * @remark Windows treat internally everything as UTF-16. * The function is using directly the Win32 API * for outputting the wide-string as UTF-16. * * @note The console must be using a Unicode enabled font, such * as Lucida Console. The function is NOT working when stdout * is redirected. * * @sa putws_unicode() ************************************************************* */ int printws_unicode( const wchar_t *ws ) { DWORD ret = 0; /* # of characters written */ /* sanity checks */ if ( !ws || !(*ws) ) return 0; if ( !WriteConsoleW( GetStdHandle(STD_OUTPUT_HANDLE), ws, wcslen(ws), &ret, NULL) ) return WEOF; return (int)ret; } /*********************************************************//** * @par Prototype * int wprintf_unicode( const wchar_t *wformat, ... ); * * @brief Unicode, code-page agnostic, formatted output of a * wide-string to the console * ( like a Unicode version of wprintf() ). * * @param[in] wformat The formating template wide-string, used as in wprintf(). * @param[in] ... A comma-separated list of objects to be outputted, according * to the wformat argument ( used as in wprintf() ). * * @return The number of characters successfully outputted, * or -1 on error. * * @remark Windows treat internally everything as UTF-16LE. * The function is using direclty the Win32API to convert * any wchar_t to UTL-16LE & output it to the console. * * @note Wide-strings resulting in more than 1024 characters * (excluding the nul-terminator) are ouputted up to that limit! * \n * The console must be using a Unicode enabled font, such * as Lucida Console. The function is NOT working when stdout * is redirected. * * @sa printws_unicode(), putws_unicode(). ************************************************************* */ int wprintf_unicode( const wchar_t *wformat, ... ) { va_list args; int ret = 0; /* # of bytes written */ wchar_t *wstrout = NULL; size_t lenwstrout = 1024+1; /* sanity check */ if ( !wformat ) return -1; wstrout = calloc( lenwstrout, sizeof(wchar_t)); if ( !wstrout ) return -1; va_start( args, wformat ); if ( (ret=vswprintf( wstrout, lenwstrout-1, wformat, args )) < 0 ) { free( wstrout ); va_end( args ); return ret; } va_end( args ); if ( !WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), wstrout, wcslen(wstrout), (DWORD *)&ret, NULL) ) ret = -1; free( wstrout ); return (int)ret; } /*********************************************************//** * @par Prototype * int wprint_unicode_range( const wchar_t *label, const wchar_t wcstart, const wchar_t wcend ); * * @brief Unicode, code-page agnostic, output of an ordered series * of wide-characters (useful for printing contiguous parts * of the Unicode table). * * @param[in] wformat The formating template wide-string, used as in wprintf(). * @param[in] ... A list of addresses to the variables specified in wformat, * used as in wprintf(). * * @return The number of characters successfully outputted, * or -1 on error. * * @remark Windows treat internally everything as UTF-16LE. * The function is using direclty the Win32API to convert * any wchar_t to UTL-16LE & output it to the console. * * @note The console must be using a Unicode enabled font, such * as Lucida Console. The function is NOT working when stdout * is redirected. ************************************************************* */ int wprint_unicode_range( const wchar_t *label, const wchar_t wcstart, const wchar_t wcend ) { wchar_t wc; int failed = 0; if ( label && *label ) printws_unicode( label ); for (wc = wcstart; !failed && wc <= wcend; wc++) failed = WEOF == putwchar_unicode(wc); if ( !failed ) putwchar_unicode(L'\n'); return failed; } /*********************************************************//** * ************************************************************* */ int main( void ) { wchar_t weuro[10+1] = {L'€'}; putws_unicode(L"Primitive, code-page agnostic, wchar_t wrapper for Unicode output on Windows\nconsole (using the Win32API for direct conversions from wchar_t to UTF-16LE).\n"); putws_unicode(L"1) Single character using putwchar_unicode():"); putwchar_unicode(L'\t'); putwchar_unicode(L'ϊ'); putws_unicode(L"\n"); putws_unicode(L"2) Strings & String-literals using putws_unicode() or printws_unicode():"); putws_unicode(L"\thello|γεια|Здравствуйте|Grüßgott|hyvää päivää|hæ\n"); printws_unicode(L"3) Formated output using wprintf_unicode():\n"); wcscpy( weuro, L"€" ); wprintf_unicode( L"\tEuro sign: %ls\n\n", weuro ); putws_unicode(L"4) Unicode ranges (with optional label) using wprint_unicode_range():"); wprint_unicode_range( L"\nLabel: Greek & Coptic Unicode...\n",/* optional header text */ L'\u0370', /* starting unicode char */ L'\u03FF' /* ending unicode char */ ); wprint_unicode_range( L"\nLabel: Spacing Modifiers Unicode Table (needs Consolas font)...\n", L'\u02B0', /* starting unicode char */ L'\u02FF' /* ending unicode char */ ); putwchar(L'\n'); system("pause"); exit( EXIT_SUCCESS ); } Αν δεν υπάρχουν προβλήματα, θα προσπαθήσω να φτιάξω και το αντίστροφο για είσοδο (και μετά αποκλειστικά για utf-8, που στην κονσόλα των Windows δεν είναι ακριβώς πραγματική... προσομοιώνεται με ANSI (multi-byte) strings ως ξεχωριστή κωδικοσελίδα, οπότε μπορεί να διαχειριστεί με narrrow-strings). Έξοδος του παραπάνω κώδικα, σε κονσόλα των Windows 7/64bit με γραμματοσειρά Consolas, πριν και μετά τις διορθώσεις... EDIT3: Διόρθωση της putwchar_unicode() κατόπιν σωστότατης υπόδειξης του φίλου παπι! Επίσης, άλλαξα τους τύπους και τις τιμές επιστροφής των συναρτήσεων, ώστε να είναι όσο το δυνατόν πιο κοντά στις στάνταρ w* συναρτήσειςτης γλώσσας (τις οποίες και προσπαθούν να προσομοιώσουν ως Unicode). EDIT2: Και πάλι κατόπιν υπόδειξης του φίλου DirectX, προστέθηκε και η BorlandC στα #if-#elif της vswprintf() στην αρχή του κώδικα! EDIT1: Προσθήκη της ξεχασμένης: free( wstrout ); στην wprintf_unicode() κατόπιν σωστότατης υπόδειξης του φίλου DirectX! Επεξ/σία 11 Φεβρουαρίου 2012 από migf1
Directx Δημοσ. 10 Φεβρουαρίου 2012 Δημοσ. 10 Φεβρουαρίου 2012 Φίλε migf1 αυτή είναι η έξοδος σε Windows XP SP3 με Lucida Console σε VS08 (έγινε άμεσα compile): και με raster φόντο: Υ.Γ. 1. Κατά παρόμοιο τρόπο λειτουργεί και σε C++ Builder 2010 με την διαφορά ότι στην γραμμή 156 πρέπει να αφαιρεθεί από την vswprintf το όρισμα lenwstrout-1 - μπορεί να λυθεί ως: > #ifdef __BORLANDC__ if ( vswprintf( wstrout, wformat, args ) < 0 ) { #else if ( vswprintf( wstrout, lenwstrout-1, wformat, args ) < 0 ) { #endif va_end( args ); return 0; } 2. Προσοχή σε θέματα διαχείρισης μνήμης (βλ. wstrout στην wprintf_unicode) καθώς δεν αποδεσμεύεις πάντα.
migf1 Δημοσ. 10 Φεβρουαρίου 2012 Μέλος Δημοσ. 10 Φεβρουαρίου 2012 Φίλε migf1 αυτή είναι η έξοδος σε Windows XP SP3 με Lucida Console σε VS08: Τέλειο, σε ευχαριστώ πολύ! Έτσι πρέπει να δείχνει με Lucida Console (με Consolas στα 7άρια δείχνει λιγότερα άδεια κουτάκια). Ακόμα και με Lucida όπως δείχνει η εικόνα, λογικά πρέπει να υπάρχει σωστό το code-point του κάθε άδειου κουτιού... επιβεβαίωση/διάψευση γίνεται με copy & paste των άδειων κουτιών από την κονσόλα σε ένα fully unicode enabled editor/IDE, όπου εκεί θα πρέπει να εμφανιστούν τα σωστά σχήματα. Αν δεν είναι κόπος, ας δοκιμάσει κάποιος με εκτελέσιμο από VS σε διαφορετικά code-page της κονσόλας... π.χ. chcp 737, chcp 65001, chcp 1253 2. Προσοχή σε θέματα διαχείρισης μνήμης (βλ. wstrout στην wprintf_unicode) καθώς δεν αποδεσμεύεις πάντα. Αμάν, έχω ξεχάσει το free() εκεί!!! Thanks, μισό να το προσθέσω! Υ.Γ. 1. Κατά παρόμοιο τρόπο λειτουργεί και σε C++ Builder 2010 με την διαφορά ότι στην γραμμή 156 πρέπει να αφαιρεθεί από την vswprintf το όρισμα lenwstrout-1 - μπορεί να λυθεί ως: > #ifdef __BORLANDC__ if ( vswprintf( wstrout, wformat, args ) < 0 ) { #else if ( vswprintf( wstrout, lenwstrout-1, wformat, args ) < 0 ) { #endif va_end( args ); return 0; } Μήπως έχει η Borland vswnprintf() ή _vsnwprintf() ή vsnwprintf_s() για όρισμα μήκους, ώστε απλά να την προσθέσω κι αυτή στο αρχικό #if του κώδικα;
Directx Δημοσ. 10 Φεβρουαρίου 2012 Δημοσ. 10 Φεβρουαρίου 2012 [..]Αν δεν είναι κόπος, ας δοκιμάσει κάποιος με εκτελέσιμο από VS σε διαφορετικά code-page της κονσόλας... π.χ. chcp 737, chcp 65001, chcp 1253 Από ότι είδα σε Windows XP SP3, το CHCP δεν φαίνεται να επηρεάζει την έξοδο των χαρακτήρων. Μήπως έχει η Borland vswnprintf() ή _vsnwprintf() ή vsnwprintf_s() για όρισμα μήκους, ώστε απλά να την προσθέσω κι αυτή στο αρχικό #if του κώδικα; Ναι υπάρχει το _vsnwprintf Οπότε.. || defined(__BORLANDC__)
migf1 Δημοσ. 10 Φεβρουαρίου 2012 Μέλος Δημοσ. 10 Φεβρουαρίου 2012 Από ότι είδα σε Windows XP SP3, το CHCP δεν φαίνεται να επηρεάζει την έξοδο των χαρακτήρων. Σούπερ! Ναι υπάρχει το _vsnwprintf Οπότε.. || defined(__BORLANDC__) Ευχαριστώ! Τις έβαλα και τις 2 διορθώσεις στον κώδικα στο αρχικό post. EDIT: Γνωρίζει κανείς αν η κονσόλα των Windows μπορεί να διαχειριστεί UTF-16 surrogates με ή χωρίς συναρτήσεις του Win32 Api; Επίσης, έχω ένα θέμα με το πως μπορώ να αναγνωρίσω πόσα από τα 2 UTF-16LE bytes που χρησιμομοποιεί η κονσόλα κρατάνε χρήσιμες πληροφορίες. Το χρειάζομαι για βελτίωση της συνάρτησης: putcwchar_unicode( const wchar_t wchar); η οποία όπως είναι τώρα υλοποιημένη στον κώδικα, με εκτελέσιμο από mingw32 gcc τυπώνει πάντα 2 χαρακτήρες, 1 για κάθε byte... αν το 2ο byte δεν χρησιμοποιείται το τυπώνει ως άγνωστο χαρακτήρα. Αν παρατηρήσετε στον κώδικα, αυτός είναι ο λόγος που στην wprint_unicode_range() δεν τυπώνω τους μεμονωμένους χαρακτήρες με putwchar_unicode() αλλά με wprintf_unicode().
παπι Δημοσ. 11 Φεβρουαρίου 2012 Δημοσ. 11 Φεβρουαρίου 2012 Επίσης, έχω ένα θέμα με το πως μπορώ να αναγνωρίσω πόσα από τα 2 UTF-16LE bytes που χρησιμομοποιεί η κονσόλα κρατάνε χρήσιμες πληροφορίες. Το χρειάζομαι για βελτίωση της συνάρτησης: putcwchar_unicode( const wchar_t wchar); η οποία όπως είναι τώρα υλοποιημένη στον κώδικα, με εκτελέσιμο από mingw32 gcc τυπώνει πάντα 2 χαρακτήρες, 1 για κάθε byte... αν το 2ο byte δεν χρησιμοποιείται το τυπώνει ως άγνωστο χαρακτήρα. Μαλλον μπερδευτηκες. Τα wide strings δεν μετριουνται σε byte αλλα σε wide chars. Αρα αντι το sizeof(wchar_t) βαζεις 1 και καθαρισες.
Directx Δημοσ. 11 Φεβρουαρίου 2012 Δημοσ. 11 Φεβρουαρίου 2012 Έχει δίκιο ο παπι, βάση της τεκμηρίωσης του WriteConsole(W) το τρίτο όρισμα (nNumberOfCharsToWrite) μετρά σε χαρακτήρες.
migf1 Δημοσ. 11 Φεβρουαρίου 2012 Μέλος Δημοσ. 11 Φεβρουαρίου 2012 Σαφώς κι έχει δίκιο ο φίλος πάπι! Ευχαριστώ πολύ παιδιά για τις διορθώσεις, και το ενδιαφέρον! Διόρθωσα και τον κώδικα κι άλλαξα και τις τιμές επιστροφής των συναρτήσεων.
defacer Δημοσ. 14 Φεβρουαρίου 2012 Δημοσ. 14 Φεβρουαρίου 2012 @mig: Έχε υπόψη ότι πουθενά δεν ορίζεται τι ακριβώς σημαίνει wchar_t, πόσο μάλλον το τι encoding χρησιμοποιείται εσωτερικά για την αναπαράσταση των χαρακτήρων (αυτό νομίζω ονομάζεται transfer encoding). Αυτό σημαίνει μεταξύ άλλων ότι μια signature >putcwchar_unicode( const wchar_t* wchar) είναι ήδη "λάθος" με την έννοια ότι στη γενική περίπτωση δεν έχεις ιδέα του τι ακριβώς bitwise αναπαράσταση έχουν αυτά τα wchar_t που σου περνάνε. Γενικά η όλη φάση με το wchar_t στην C/C++ είναι τουλάχιστον τραγική γιατί είναι τόσο under-specified που πρακτική χρήση δεν παίζει όταν έχεις να δουλέψεις με κείμενο. Μια τεχνικά ανώτερη λύση θα ήταν να χρησιμοποιείς απλό char* μαζί με length και encoding flag παντού, και να χρησιμοποιήσεις κάποια βιβλιοθήκη όπως η ICU όταν θέλεις να κάνεις encoding conversion.
migf1 Δημοσ. 14 Φεβρουαρίου 2012 Μέλος Δημοσ. 14 Φεβρουαρίου 2012 Καλησπέρα, ναι, είμαι ενήμερος για την compiler dependent υλοποίηση του wchar_t, για αυτό και δεν επιχείρησα να το διαχειριστώ μονάχος και χρησιμοποίησα την WriteConsoleW() του Wind32API. Στα Windows το wchar_t είναι εγγυημένα 16-bits. Θα μπορούσε να μπει κι ένα... >typedef wchar_t TCHAR αλλά είναι πολύ messy νομίζω (γιατί παραπέμπει σε συγκεκριμένες διαδικασίες, τις οποίες δεν τις ακολουθεί ο κώδικάς μου). ΥΓ. Η είσοδος είναι μανίκι
defacer Δημοσ. 14 Φεβρουαρίου 2012 Δημοσ. 14 Φεβρουαρίου 2012 Γενικά το θέμα αυτό είναι μανίκι στα Windows, έχει καεί και η δική μου γούνα. Πάντως για surrogates που ρώτησες, το θέμα δεν είναι μόνο αν μπορεί να τα δεχτεί η κονσόλα αλλά και το αν actually μπορεί να τα δημιουργήσει o compiler (αφού στα χαρτιά "ένα wchar_t μπορεί να αναπαραστήσει ένα unicode χαρακτήρα" τότε τι γίνεται στην περίπτωση που είμαστε εκτός BMP? δεν το έχω δοκιμάσει ποτέ, εξάλλου είναι implementation-dependent).
migf1 Δημοσ. 14 Φεβρουαρίου 2012 Μέλος Δημοσ. 14 Φεβρουαρίου 2012 Γενικά το θέμα αυτό είναι μανίκι στα Windows, έχει καεί και η δική μου γούνα. Πάντως για surrogates που ρώτησες, το θέμα δεν είναι μόνο αν μπορεί να τα δεχτεί η κονσόλα αλλά και το αν actually μπορεί να τα δημιουργήσει o compiler (αφού στα χαρτιά "ένα wchar_t μπορεί να αναπαραστήσει ένα unicode χαρακτήρα" τότε τι γίνεται στην περίπτωση που είμαστε εκτός BMP? δεν το έχω δοκιμάσει ποτέ, εξάλλου είναι implementation-dependent). Η βασική ιδέα ήταν (και παραμένει) ένας code-page agnostic, wchar_t wrapper του Win32API για Unicode είσοδο/έξοδο προς/από την κονσόλα των Windows. Οι εγγυήσεις που έχουμε είναι πως το wchar_t στα Windows είναι 16 bits και πως εσωτερικά το Win32API διαχειρίζεται τα πάντα ως UTF-16LE. Το πρόβλημα είναι πως δεν είμαι εξοικειωμένος με τις σχετικές συναρτήσεις του Win32API, οπότε πρέπει να κάτσω να ρίξω αρκετό διάβασμα. Στην έξοδο φάνηκα τυχερός, επειδή βρήκα σχετικά γρήγορα πως το Win32API έχει συναρτήσεις που αναλαμβάνουν μόνες τους να μετατρέψουν ένα (wchar_t *) σε (utf16le *) και να το κάνουν και output στην κονσόλα. Για την είσοδο σε μια πρόχειρη ματιά που έριξα, τα πράγματα είναι αρεκτά πιο σύνθετα και μέχρι στιγμής δεν έχει πάρει το μάτι μου αντίστοιχη συνάρτηση με την WriteConsoleW() που να είναι το ίδιο έυχρηστη. Π.χ. η ReadConsoleInput() που είδα, προϋποθέτει πολλή περισσότερη δουλειά. Για αυτό ρώτησα αν ξέρει κανείς συναρτήσεις (ιδανικά να διαχειρίζονται και surrogates) μπας και γλιτώσω χρόνο και κόπο EDIT: Βασικά το typedef που γράφω στο προηγούμενο ποστ, μπορεί να αλλάξει και σε... > #include <stdint.h> // C99 typedef wchar_t int16_t;
παπι Δημοσ. 14 Φεβρουαρίου 2012 Δημοσ. 14 Φεβρουαρίου 2012 Για να εχει το κεφαλι σου ησυχο φτιαξε μια κονσολα. Δες θετικα 1) Ζωγραφιζεις οποιο codepage θελεις οπως θελεις 2) Θα εχεις συναρτηση που θα φλασαρει ιντερναλικα τον κονσολα μπαφερ 3) Θα πιανεις ολα τα κεϊμπορδ ευεντς και μαους ευεντς 4) οχι πια 64 κιλο μπάιτ μπαφερ κλπ κλπ
migf1 Δημοσ. 14 Φεβρουαρίου 2012 Μέλος Δημοσ. 14 Φεβρουαρίου 2012 Για να εχει το κεφαλι σου ησυχο φτιαξε μια κονσολα. Δες θετικα 1) Ζωγραφιζεις οποιο codepage θελεις οπως θελεις 2) Θα εχεις συναρτηση που θα φλασαρει ιντερναλικα τον κονσολα μπαφερ 3) Θα πιανεις ολα τα κεϊμπορδ ευεντς και μαους ευεντς 4) οχι πια 64 κιλο μπάιτ μπαφερ κλπ κλπ Αυτά ήθελα να αποφύγω ρε συ! Μέχρι στιγμής δεν το βλέπω να μπορώ Νομίζω πάντως πως δεν χρειάζεται να φτιάξεις ξεχωριστή κονσόλα για τα 1-2-3... μπορείς και στην στάνταρ, έτσι δεν είναι; Παρεμπιπτόντως, η παρακάτω ρουτίνα δείχνει να λύνει το θέμα του redirection στην έξοδο, αλλά δεν είναι code-page agnostic. Αντιθέτως, μετατρέπει τα πάντα στην τρέχουσα κωδικοσελίδα της κονσόλας... > DWORD w_print( const wchar_t *wtext ) { DWORD ret; /* # of bytes writetn */ DWORD outMode; /* current mode of stdout */ HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); /* get handle of stdout */ UINT outCP; /* console's output code-page*/ char *strout = NULL; /* mb string (ansi) to output*/ int lenstrout = 0; /* length of strout in chars */ /* is stdout a character file (console or LPT) with valid output mode? */ if( (GetFileType(hOut) & FILE_TYPE_CHAR) && GetConsoleMode(hOut, &outMode) ) { WriteConsoleW( hOut, wtext, wcslen(wtext), &ret, 0 ); } /* stdout is either disk or pipe, take redirection into account */ else { /* get console's output CP */ outCP = GetConsoleOutputCP(); /* get the length needed for converting wtext to mb strout */ lenstrout = WideCharToMultiByte(outCP, 0, wtext, -1, 0, 0, NULL, NULL); /* create the mb strout & convert wtext to strout */ strout = malloc(lenstrout * sizeof(char)); WideCharToMultiByte(outCP, 0, wtext, -1, strout, lenstrout, NULL,NULL); /* write the mb strout to hOut, without the nil-terminator char */ WriteFile( hOut, strout, lenstrout-1, &ret, NULL ); free( strout ); } /* Note: * WriteFile writes charCount-1 bytes to filter out the NULL terminator. */ return ret; }
defacer Δημοσ. 14 Φεβρουαρίου 2012 Δημοσ. 14 Φεβρουαρίου 2012 Οι εγγυήσεις που έχουμε είναι πως το wchar_t στα Windows είναι 16 bits και πως εσωτερικά το Win32API διαχειρίζεται τα πάντα ως UTF-16LE. Αφού πάει με τον compiler (βασικά με το runtime) και όχι με το λειτουργικό, πώς έχουμε εγγύηση ότι είναι ακριβώς 16 bits? Γενικά το point μου είναι πως χρησιμοποιώντας wchar_t απο εμπειρία μου πιστεύω ότι δημιουργείς περισσότερα προβλήματα απ' ότι λύνεις. Απλώς όταν είσαι αποκλειστικά σε VS και Windows τυχαίνει να δουλεύουν. Link που μόλις έκανα google και αναφέρεται στα θέματα που θίγω. Και ένα δεύτερο, που μάλλον είναι καλύτερο. (BTW σχετικά με το τελευταίο link στη FirstObject: αν δουλεύετε με XML έχουν ένα δωρεάν πρόγραμμα που για την τιμή του απλά τα σπάει).
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα