Es ist unmöglich, ohne ein gutes Minimal, Complete, and Verifiable code example sicher zu wissen. Aber es sieht für mich so aus, als ob Sie den falschen Konverter in C++ verwenden.
Das Gebietsschema std::codecvt_utf8<wchar_t>
konvertiert von UCS-2, nicht UTF-16. Die beiden sind sehr ähnlich, aber UCS-2 unterstützt keine Ersatzpaare, die zum Codieren des zu codierenden Zeichens erforderlich wären.
Stattdessen sollten Sie std::codecvt_utf8_utf16<wchar_t>
werden:
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> utf8Converter;
std::string utf8str = utf8Converter.to_bytes(wstr);
Als ich den Konverter verwenden, erhalte ich die UTF-8-Bytes benötigt: F0 9F 8C 8E
. Diese dekodieren natürlich in .NET korrekt, wenn sie als UTF-8 interpretiert werden.
Nachtrag:
Die Frage wurde aktualisiert, um anzuzeigen, dass die Code-Codierung kann nicht geändert werden. Sie stecken mit UCS-2 fest, das in ungültiges UTF8 codiert wurde. Da UTF8 ungültig ist, müssen Sie den Text selbst dekodieren.
Ich sehe ein paar vernünftige Möglichkeiten, dies zu tun. Schreiben Sie zuerst einen Decoder, der sich nicht darum kümmert, ob der UTF8 ungültige Bytefolgen enthält. Zweitens, verwenden Sie den C++ std::wstring_convert<std::codecvt_utf8<wchar_t>>
Konverter, um die Bytes für Sie zu dekodieren (z. B. schreiben Sie Ihren Empfangscode in C++ oder schreiben Sie eine C++ DLL, die Sie aus Ihrem C# -Code aufrufen können, um die Arbeit zu erledigen).
Die zweite Option ist in gewissem Sinne die zuverlässigere, d. H. Sie verwenden genau den Decoder, der die schlechten Daten überhaupt erstellt hat. Auf der anderen Seite könnte es sogar zu übertrieben sein, eine DLL zu erstellen, geschweige denn den gesamten Client in C++ schreiben. Wenn Sie eine DLL erstellen, sogar mit C++/CLI, haben Sie immer noch einige Probleme damit, die Interoperabilität zu verbessern, es sei denn, Sie sind bereits ein Experte.
Ich bin vertraut, aber kaum ein Experte, mit C++/CLI. Ich bin viel besser mit C#, also hier ist ein Code für die erste Option:
private const int _khighOffset = 0xD800 - (0x10000 >> 10);
/// <summary>
/// Decodes a nominally UTF8 byte sequence as UTF16. Ignores all data errors
/// except those which prevent coherent interpretation of the input data.
/// Input with invalid-but-decodable UTF8 sequences will be decoded without
/// error, and may lead to invalid UTF16.
/// </summary>
/// <param name="bytes">The UTF8 byte sequence to decode</param>
/// <returns>A string value representing the decoded UTF8</returns>
/// <remarks>
/// This method has not been thoroughly validated. It should be tested
/// carefully with a broad range of inputs (the entire UTF16 code point
/// range would not be unreasonable) before being used in any sort of
/// production environment.
/// </remarks>
private static string DecodeUtf8WithOverlong(byte[] bytes)
{
List<char> result = new List<char>();
int continuationCount = 0, continuationAccumulator = 0, highBase = 0;
char continuationBase = '\0';
for (int i = 0; i < bytes.Length; i++)
{
byte b = bytes[i];
if (b < 0x80)
{
result.Add((char)b);
continue;
}
if (b < 0xC0)
{
// Byte values in this range are used only as continuation bytes.
// If we aren't expecting any continuation bytes, then the input
// is invalid beyond repair.
if (continuationCount == 0)
{
throw new ArgumentException("invalid encoding");
}
// Each continuation byte represents 6 bits of the actual
// character value
continuationAccumulator <<= 6;
continuationAccumulator |= (b - 0x80);
if (--continuationCount == 0)
{
continuationAccumulator += highBase;
if (continuationAccumulator > 0xffff)
{
// Code point requires more than 16 bits, so split into surrogate pair
char highSurrogate = (char)(_khighOffset + (continuationAccumulator >> 10)),
lowSurrogate = (char)(0xDC00 + (continuationAccumulator & 0x3FF));
result.Add(highSurrogate);
result.Add(lowSurrogate);
}
else
{
result.Add((char)(continuationBase | continuationAccumulator));
}
continuationAccumulator = 0;
continuationBase = '\0';
highBase = 0;
}
continue;
}
if (b < 0xE0)
{
continuationCount = 1;
continuationBase = (char)((b - 0xC0) * 0x0040);
continue;
}
if (b < 0xF0)
{
continuationCount = 2;
continuationBase = (char)(b == 0xE0 ? 0x0800 : (b - 0xE0) * 0x1000);
continue;
}
if (b < 0xF8)
{
continuationCount = 3;
highBase = (b - 0xF0) * 0x00040000;
continue;
}
if (b < 0xFC)
{
continuationCount = 4;
highBase = (b - 0xF8) * 0x01000000;
continue;
}
if (b < 0xFE)
{
continuationCount = 5;
highBase = (b - 0xFC) * 0x40000000;
continue;
}
// byte values of 0xFE and 0xFF are invalid
throw new ArgumentException("invalid encoding");
}
return new string(result.ToArray());
}
ich es mit Ihrem Globus Charakter getestet und es funktioniert für das in Ordnung. Es dekodiert auch richtig das UTF8 für dieses Zeichen (d. H. F0 9F 8C 8E
).Natürlich möchten Sie es mit einer ganzen Reihe von Daten testen, wenn Sie diesen Code für die Decodierung all Ihrer UTF8-Eingaben verwenden möchten.
Ich bekomme '0xD83C 0xDF0E' für das Zeichen, nicht' 0xD83D 0xDF0E', wie Sie behaupten. Außerdem, wenn ich .NET verwende, um dieses Zeichen als UTF8 zu kodieren, bekomme ich 'F0 9F 8C 8E', nicht 'ED A0 BC ED BC 8E', wie Sie behaupten. Schließlich, wenn ich 'F0 9F 8C 8E' zurück zu einer C# Zeichenkette decodiere, bekomme ich das' '' 'Ich begann mit, und das kodiert in UTF16 als das ursprüngliche' 0xD83C 0xDF0E', genau wie erwartet. Bitte stellen Sie eine gute [mcve] bereit, die Ihr Problem zuverlässig reproduziert. Im Moment scheint das nichts anderes zu sein als ein Problem mit der Konvertierung von Code in UTF8 (die nicht wie C# aussieht ... es scheint C++ zu sein). –
Ersatzcodepunkte * können nicht in UTF-8 (oder einem beliebigen UTF) codiert werden, daher ersetzt 'Encoding.UTF8.GetString' die ungültigen Bytes durch' U + FFFD'. Wie Sie aussehen [CESU-8] (http://www.unicode.org/reports/tr26/). –
@PeterDuniho: Zeichen korrigiert sorry. Ich fügte eine Probe hinzu und stellte klar, dass ich das Produktionsprogramm nicht mehr kontrolliere. – Jonathan