String encoding



  • Hallo,

    mein Programm hat verschiedene Schnittstellen, die Strings liefern. Daher arbeite ich intern mit utf-8 un konvertiere alles entsprechend (per iconv). Zwei Schnittstellen fehlen mir noch, weil ich momentan nicht weiß, wie ich deren codierung auslesen kann:

    1. Konsole, d.h. argv, und Ausgaben in die Konsole
    2. Dateisystem: open(), fstream.
      Kann ich davon ausgehen, dass 1) und 2) immer die Gleiche Codierung verwenden? Ist zumindset auf allen Systemen so, die ich getestet habe.

    Über die Locales kann ich den Zeichensatz auch nicht direkt bestimmen, oder? Zumindest unter Windows sieht die gelieferte Locale nicht danach aus (German_Germany.1252), was iconv nicht akzeptiert, das müsste ich umwandlen können in CP1252.

    Per Iconv kann ich nur umwandeln, wenn ich die Zielcodierung kenne. Beim meinem Linux-System ist die Konsole utf-8 und ich brauche nichts umwandeln, bei Windows muss ich umwandeln, sonst kann ich keine Dateien mit Umlauten öffnen...

    Irgendwie finde ich zu dem Thema wenig brauchbares, müsste doch ein allgemeines Thema sein!?

    Habt ihr Tipps für mich?

    Danke + Grüße
    Mattes



  • Auf der Konsole arbeitest du AFAIK immer mit ASCII + Windows-Codepages. Wenn du eine Datei öffnest, hängt es natürlich davon ab, in welchem Encoding die Datei gespeichert wurde. Du kannst Dateien auch in UTF-8, UTF-16 oder UTF-32 speichern. Du kannst dir nicht immer 100%ig sicher sein, in welchem Format die Datei gespeichert ist, aber du kannst nach einer Byte Order Mark (BOM) schauen, falls es keine gibt, sollte es entweder UTF-8 oder ASCII sein und dann kannst du noch überprüfen, ob der Text valides UTF-8 wäre. Die Funktion dazu habe ich gerade nicht zur Hand, kann sie dir aber bei Gelegenheit mal senden. Falls der Text nur ASCII ohne 128-255 ist, wird dir die Funktion zwar zurückliefern, dass es UTF-8 ist, aber das kommt ja sowieso auf das Gleiche heraus.



  • Es ist möglich, Umlaute und Sonderzeichen auf der Konsole auszugeben.

    const unsigned char AE = static_cast<unsigned char>(142);
    const unsigned char ae = static_cast<unsigned char>(132);
    const unsigned char OE = static_cast<unsigned char>(153);
    const unsigned char oe = static_cast<unsigned char>(148);
    const unsigned char UE = static_cast<unsigned char>(154);
    const unsigned char ue = static_cast<unsigned char>(129);
    const unsigned char ss = static_cast<unsigned char>(225);
    


  • wxSkip schrieb:

    Auf der Konsole arbeitest du AFAIK immer mit ASCII + Windows-Codepages.

    genau das ist die Frage: welche Codepage wird verwendet... Und was du schreibst gillt nur für Windows (und da evtl nicht mal weltweit..) wir reden hier von portablem Code!

    wxSkip schrieb:

    Wenn du eine Datei öffnest, hängt es natürlich davon ab, in welchem Encoding die Datei gespeichert wurde.

    Es geht bei 2) um Dateinamen nicht um Dateiinhalt! Sprich woher weiß ich welches Encoding fopen() etc. erwartet?



  • 314159265358979 schrieb:

    Es ist möglich, Umlaute und Sonderzeichen auf der Konsole auszugeben.

    const unsigned char AE = static_cast<unsigned char>(142);
    const unsigned char ae = static_cast<unsigned char>(132);
    const unsigned char OE = static_cast<unsigned char>(153);
    const unsigned char oe = static_cast<unsigned char>(148);
    const unsigned char UE = static_cast<unsigned char>(154);
    const unsigned char ue = static_cast<unsigned char>(129);
    const unsigned char ss = static_cast<unsigned char>(225);
    

    nicht ganz das was ich wissen wollte 😉

    ich hatte schon die Vermutung dass

    iconv_t cd = iconv_open ("char", "UTF-8");
    

    die Lösung sein könnte (umwndlung von utf-8 in Consolen-encoding), aber unter Linux mag es garnicht (errno == EINVAL), und unter Windows tut es bei der conversation dann garnichts, es kommt der Eingabestring einfach wieder raus. Ist wohl nur für char zu w_char oder umgekehrt zu gebrauchen.
    sowas

    iconv_t cd = iconv_open ("UTF-8", "UTF-8");
    

    geht interessanterweise, wobei natürlich wieder das gleiche rauskommt was reinging, nur mal so am Rande.

    Also bin ich immer noch nicht weiter. Aber das muss doch gehen. Eine Möglichkeit wäre wohl eine Lookuptable von LC_CTYPE-Locale zu Codierung, wobei ich das eigentlich nicht machen will. Ginge natürlich für meine Anwendungfälle, ist aber wieder nicht wirklich portabel.

    Vielleicht noch zur Erklärung: ich will das schlank halten, weil es eine Bibliothek ist, die ich z.B auch in Python verwende, sonst würde ich GLIb oder QT verwenden und hätte das Problem garnicht. Aber mit purem C/C++ ists garnicht so einfach, für mich zumindest 🙄

    Viele Grüße
    Mattes

    Edit 1: Typo
    Edit 2: Erklärung





  • Hier mal ein Beispiel das praktisch funktioniert:

    const std::string getCharset()
    {
    	std::string Locale;
    	std::string charset;
    	if (!setlocale(LC_ALL, "")) {
    		fprintf(stderr, "Can't set the specified locale! Check LANG, LC_CTYPE, LC_ALL.\n");
    		return(std::string());
    		//TODO: throw exception
    	}
    	else{
    		Locale = setlocale(LC_CTYPE,NULL);
    		std::cout<<"Locale LC_CTYPE is: "<<Locale<<std::endl;
    	}
    	std::string LocaleLower = convertLowercase(Locale);
    	std::cout<<" actual CTYPE Locale (in lowecase) is: "<<LocaleLower<<std::endl;
    	int pinLoc = countOccurence(Locale,std::string("."));
    	int ulinLoc = coutOccurence(Locale,std::string("_"));
    	std::cout<<"found . "<<pinLoc<<" times, "<<"found _ "<<ulinLoc<<" times in "<<Locale<<std::endl;
    	std::string codepage;
    	if(pinLoc == 1){
    		size_t pos = Locale.find(std::string("."),0);
    		codepage = Locale.substr(pos+1,Locale.length()-pos-1); //TODO: anhängsel @... z.B. @euro
    		std::cout<<"codepage is: "<<codepage<<std::endl;
    	}
    	else{
    		std::cout<<"unable to find codepage in Locale!"<<std::endl;
    	}
    	if(ulinLoc == 1){
    		size_t posul = Locale.find(std::string("_"),0);
    		std::string Language = Locale.substr(0,posul);
    		std::string Country;
    		if(pinLoc == 1){
    			size_t posp = Locale.find(std::string("."),0);
    			Country = Locale.substr(posul+1,posp-posul-1);
    		}
    		else{
    			Country = Locale.substr(posul+1,Locale.length()-posul-1);//TODO: anhängsel @... z.B. @euro
    		}
    		std::cout<<"Country is: "<<Country<<", Language is: "<<Language<<std::endl;
    	}
    	else{
    		std::cout<<"unable to find Country/Language in Locale!"<<std::endl;
    	}
    	if(codepage.empty())
    		return(std::string("unkown"));
    
    	if(codepage == std::string("1252"))
    		charset = std::string("CP1252");
    	else if(codepage == std::string("UTF-8"))
    		charset = std::string("UTF-8");
    	else
    		charset = std::string("unkown");
    
    	#if defined(_WIN32) || defined(WIN32) || defined (WIN) || defined(_WIN) ||  defined (_WIN64) || defined (WIN64)
    		std::string consoleCodepage;
    		char buffer[512];
    		memset(buffer,0,sizeof(buffer));
    		FILE *fp = popen("chcp", "r" );
    		if(fgets( buffer, sizeof(buffer), fp ) != NULL) {
          		consoleCodepage = std::string(buffer);
    		}
    		pclose( fp );
    		std::cout<<"consoleCodepage before: "<<consoleCodepage<<std::endl;
    		if(system(std::string("chcp ").append(codepage).c_str())){
    			std::cout<<"cannot set console Codepage to "<<codepage<<std::endl;
    		}
    		else{
    			memset(buffer,0,sizeof(buffer));
    			fp = popen("chcp", "r" );
    			if ( fgets( buffer, sizeof(buffer), fp ) != NULL ) {
          			consoleCodepage = std::string(buffer);
    			}
    			pclose( fp );
    			std::cout<<"consoleCodepage after: "<<consoleCodepage<<std::endl;
    		}
    	#else 
    		//Linux:  Terminal Encoding == LC_CTYPE, hopefully ever....
    
    	// # OTHER OS?
    	#endif
    
    	return(charset);
    }
    

    Ist nicht wirklich schön, unterstützt erst zwei Codierungen, aber ihr seht wohin die Reise gehen soll..

    Edit: Für die die es proktisch probieren wollen, hier noch die hilfsfunktionen:

    int countOccurence(const std::string &searchin, const std::string &searchfor)
    {
    	size_t found = 0;
    	int occ = 0;
    	do{
    		found = searchin.find(searchfor,found+1);
    		if (found != std::string::npos){
    			++occ;
    		}
    	}while(found != std::string::npos);
    	return(occ);
    }
    const std::string convertLowercase(const std::string &inStr)
    {
    	std::locale loc;
    	size_t len = inStr.length();
    	char stringData[len];
    	strncpy(stringData, inStr.c_str(),len);
    	stringData[len]=0;
    	std::cout << "stringData: "<<stringData<<std::endl;
    	std::use_facet< std::ctype<char> >(loc).tolower(stringData, stringData+sizeof(stringData));
    	return stringData;
    }
    

Log in to reply