Charset Frage
-
@Skared sagte in Charset Frage:
Mein Problem ist das ich [u]00fc nicht in \u00fc umgewandelt bekomme ohne das Zeichen zu zerstören
so etwa:
#include <string> #include <regex> using namespace std; int main() { string alt = "das ist ein string mit [u]00fc darin"; string neu = regex_replace(alt, regex("\[u\]"), "\\u"); cout << alt << "\n" << "wird zu:\n" << neu << endl; }
da wird nichts zerstört.
-
Da [u]00fc kein Zeichen ist, kannst du es auch nicht zerstören.
Auch wenn du die drei Zeichen [u] durch die zwei Zeichen \u ersetzt, wird daraus kein ü. Auch nicht in einem wstring.
-
@manni66 sagte in Charset Frage:
Da [u]00fc kein Zeichen ist, kannst du es auch nicht zerstören.
Auch wenn du die drei Zeichen [u] durch die zwei Zeichen \u ersetzt, wird daraus kein ü. Auch nicht in einem wstring.
er will ja scheinbar ein unicode-literal haben. vielleicht will er einen quelltext generieren?
-
@Skared sagte in Charset Frage:
Mein Problem ist das ich [u]00fc nicht in \u00fc umgewandelt bekomme ohne das Zeichen zu zerstören da der Backslash escaped werden muss nehme ich an.
Was willst du denn letztendlich nach dem Umwandeln in deinem Unicode-codierten String tatsächlich stehen haben? Ein
ü
oder buchstabengetreu\u00fc
?Irgendwie erschliesst sich mir das nicht so richtig. Ich tippe mal auf das
ü
. In dem Fall ist die Lösung natürlich etwas komplizierter, da man den Code Point parsen und in eine gültige UTF-8/16-Sequenz von Code Units umwandeln muss muss.Ich habe damit mal etwas herumgespielt und meine Lösung sieht so aus (nur minimal getestet und wohl nicht super-effizient):
#ifdef _MSC_VER #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING #endif #include <cstddef> #include <cstdlib> #include <iostream> #include <string> #include <algorithm> #include <regex> #include <codecvt> #ifdef _WIN32 #if !defined(NOMINMAX) #define NOMINMAX #endif #if !defined(WIN32_LEAN_AND_MEAN) #define WIN32_LEAN_AND_MEAN #endif #include <Windows.h> #endif auto main() -> int { #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); #endif std::string json_string = "Json mit dem UE [u]00fc, dem AE [u]00e4 und der Kuh [u]1f42e ;-)"; std::regex unicode_regex("\\[u\\]([0-9abcdefABCDEF]{2,8})"); std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert; std::string utf8_string; std::size_t i = 0; std::for_each( std::sregex_iterator{ std::cbegin(json_string), std::cend(json_string), unicode_regex }, std::sregex_iterator{}, [&](std::smatch match){ // JSON-String von letzter Position bis zum Match anhängen. utf8_string.append(json_string.substr(i, match.position() - i)); // Submatch via strtoul in char32_t umwandeln, nach UTF-8 // konvertieren und anhängen. utf8_string.append( convert.to_bytes( std::strtoul(match.str(1).c_str(), nullptr, 16) ) ); i = match.position() + match.length(); } ); // Rest des JSON-String anhängen. utf8_string.append(json_string.substr(i)); std::cout << json_string << std::endl; std::cout << utf8_string << std::endl; }
Ausgabe (https://ideone.com/9REf7I):
Json mit dem UE [u]00fc, dem AE [u]00e4 und der Kuh [u]1f42e ;-) Json mit dem UE ü, dem AE ä und der Kuh 🐮 ;-)
Bezüglich Kuh: Nicht-fixe Anzahl von Hex-Ziffern ist bei der Syntax wahrscheinlich keine gute Idee, wenn auch mal Wörter wie "Äffchen" drin vorkommen sollen. Vielleicht besser das Regex zu
"\\[u\\]([0-9abcdefABCDEF]{4})"
abändernIch verwende das von @firefly angeregte
std::wstring_convert
um vom via Regex geparsten Code Point (viastd::strtoul
in einen Integer umgewandelt) nach UTF-8 zu konvertieren.Man muss allerdings erwähnen, dass
std::wstring_convert
ab C++17 deprecated ist und ich wüsste jetzt auch keine Alternative außer Drittbibliotheken.Wenn du UTF-16 brauchst, dann hat @firefly ja schon gezeigt, wie man das weiter konvertieren kann. Ich an deiner Stelle würde aber seinen Rat befolgen und wenn möglich das JSON gleich in der korrekten Kodierung erzeugen - ohne diese
[u]
-Krücke. Natürlich nur, falls das möglich ist und du Einfluss auf die Erstellung der JSON-Dateien hast.
-
Windows:
#include <cctype> #include <cstring> #include <string> #include <sstream> #include <iostream> #include <io.h> #include <fcntl.h> std::wstring strange_encoding_to_utf_16(std::string const& str) { auto const indicator{ "[u]" }; auto const indicator_length{ std::strlen(indicator) }; std::wstring result; for (std::size_t pos{ str.find(indicator) }, old_pos{}; pos != std::string::npos && pos + indicator_length + 4 <= str.length(); old_pos = pos, pos = str.find(indicator, pos)) { result += std::wstring{ str.data() + old_pos, str.data() + pos }; auto num_pos{ pos + indicator_length }; if (std::isxdigit(str[num_pos + 0]) && std::isxdigit(str[num_pos + 1]) && std::isxdigit(str[num_pos + 2]) && std::isxdigit(str[num_pos + 3])) { std::istringstream iss{ std::string{ str.data() + num_pos, str.data() + num_pos + 4 } }; unsigned long value; iss >> std::hex >> value; result += static_cast<wchar_t>(value); pos = num_pos + 4; } else { result += std::wstring{ indicator, indicator + indicator_length }; pos += indicator_length; } } return result; } int main() { static_assert(sizeof(wchar_t) != 4, "sizeof wchar_t must be 4."); _setmode(_fileno(stdout), _O_U16TEXT); std::string json_string = "Json mit dem [u]00fcfoo bar baz [u]0041[u]0042[u]0043."; std::wcout << std::wstring{ json_string.cbegin(), json_string.cend() } << L'\n' << strange_encoding_to_utf_16(json_string) << L"\n\n"; }
-
Guten morgen,
vielen Dank an alle. Ich habe mein Problem mit eurer Hilfe lösen können
Liebe Grüße
-
@Skared sagte in Charset Frage:
vielen Dank an alle. Ich habe mein Problem mit eurer Hilfe lösen können
Also doch ein
ü
und kein buchstabengetreues\u00fc
?Wichtige Erkenntnis: Diese escapten Zeichen mit Backslash werden tatsächlich nur beim Kompilieren ausgewertet um daraus die Zeichen in der jeweiligen Codierung zu generieren.
cout
oder Strings können damit nichts anfangen und geben dir diese Escape-Sequenzen exakt so aus, wie sie im String stehen.
-
@Swordfish sagte in Charset Frage:
Windows: ...
Das mutet schon etwas barock an
Interessant ist aber der der Ansatz, dass man die Umwandlung natürlich (theoretisch) etwas simpler hinbekommt, indem man die aus mehreren Code Units bestehenden UTF-16 Code Points einfach aussen vor lässt. Damit deckt man natürich nicht den gesamten Unicode-Bereich ab, aber je nach Anwendungsgebiet kann das schon ausreichen und den Code etwas vereinfachen. Vielleicht noch den zulässigen Wertebereich einschränken, damit die Funktion kein ungültiges UTF-16 erzeugen kann? Das würde ich jedenfalls nicht in die Hand desjenigen legen wollen, der die JSON-Datei schreibt
Hätte ich das manuell gemacht, anstatt es an
std::wstring_convert
zu delegieren, wäre das Ganze deutlich länger geworden.Auch das mit dem
_setmode(_fileno(stdout), _O_U16TEXT)
kannte ich noch nicht. Könnte das vielleicht eine halbwegs portable Lösung sein, um die Konsole auf UTF-16 umzustellen? Linux kennt diese Funktion meines Wissens ebenfalls (wenn auch glaube ich ohne die Unterstriche).
-
@Skared sagte in Charset Frage:
vielen Dank an alle. Ich habe mein Problem mit eurer Hilfe lösen können
Noch ein wichtiger Hinweis: Die Konvertierung in meinem Code kann einen
std::range_error
werfen (siehe hier). Das kann je nachdem wo die JSON-Daten herkommen, durchaus öfter mal passieren und sollte dein Programm nicht unbedingt "crashen" lassen.Entweder du fängst das entsprechend ab, oder konstruierst das
std::wstring_convert
-Objekt in Zeile 32 so:std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert{ "<invalid character>" };
Der im Konstruktor übergebene String wird dann in Fehlerfall in den konvertierten String eingefügt anstatt dass eine Exception geworfen wird.
-
@Finnegan sagte in Charset Frage:
Das mutet schon etwas barock an
Was gefällt Dir daran nicht? Na gut, einen stringstream auf eine immer 4-stellige Zahl zu werfen ist overkill.
-
@Swordfish sagte in Charset Frage:
@Finnegan sagte in Charset Frage:
Das mutet schon etwas barock an
Was gefällt Dir daran nicht? Na gut, einen stringstream auf eine immer 4-stellige Zahl zu werfen ist overkill.
Das, und man hätte vielleicht auch noch irgendwie den restlichen Schleifenkörper in den Kopf bekommen können
Ein
indicator
vom Typstd::string
würde den Code auch etwas übersichtlicher machen, wenn die Variable eh nichtconstexpr
ist (brauchts auch nicht wegen Effizienz, Konstantenfaltung wird das schon regeln).Die String-Kopiererei mit den ganzen
malloc
s unter der Haube hab ich allerdings auch drin. Für den Feinschliff würd ich das wohl mitstring_view
s machen. So ein Algorithmus braucht eigentlich nur auf dem Eingabe- und dem Ausgabestring zu arbeiten.
-
@Finnegan sagte in Charset Frage:
wenn die Variable eh nicht constexpr ist
Ein
constexpr
Stringliteral? Wie geht das?
-
@Swordfish sagte in Charset Frage:
@Finnegan sagte in Charset Frage:
wenn die Variable eh nicht constexpr ist
Ein
constexpr
Stringliteral? Wie geht das?Nicht das Literal selbst, sondern die Variable
indicator
(constexpr const char* indicator = "[u]";
). Gesetzt den Fallstd::strlen
wäre ebenfallsconstexpr
(theoretisch machbar, aber leider nicht der Fall, müsste man selbst implementieren), kannindicator_length
dann ebenfallsconstexpr
sein. So wie ich das sehe, könnte der Compiler am Anfang deiner Funktion ansonsten auch einen eigentlich unnötigenstrlen
-Call generieren, der dann jedes mal aufgerufen wird.Letztendlich dürften moderne Compiler jedoch schlau genug sein um auch ohne
constexpr
zu erkennen, dassindicator_length
zur Compile-Zeit berechnet werden kann und das auch tun. Besonders beistrlen
, wo Compiler meines Wissens schon lange solche Optimierungen machen. Ich hatte ja geschrieben, dass das wegen Konstantenfaltung wohl nicht wirklich nötig ist.Oder habe ich was übersehen/falsch verstanden?
-
@Finnegan sagte in Charset Frage:
Oder habe ich was übersehen/falsch verstanden?
Weiß nicht, für eine constexpr-Funktion müssen doch alle Parameter auch constexpr sein? Aber ein pointer kann nicht constexpr sein soweit ich mich erinnere?
-
@Swordfish sagte in Charset Frage:
Aber ein pointer kann nicht constexpr sein soweit ich mich erinnere?
Aber sicher:
constexpr const void * p = nullptr;
-
@wob Ich steh vielleicht auf einer dicken fetten Leitung, aber da ist doch der pointee constexpr und nicht der pointer.
-
Schau dir mal an:
int i = 42; int main() { constexpr int * p = &i; *p = 0; // geht // p = nullptr; // fails to compile: cannot assign to variable 'p' // with const-qualified type 'int *const' return *p; }
Durch mein zusätzliches
const
im Vorposting war auch der Pointeeconst
.Edit: ich hatte ehrlich gesagt überhaupt nicht über Pointer/Pointee-constness nachgedacht, sollte eigentlich auch mit dem
void * = nullptr
ein unnützer Spaß gewesen sein.
-
@wob ok, danke.