2017-09-19 37 views
9

Oto problem, którego nie mogłem rozwiązać pomimo wszystkich moich wysiłków. Więc całkowicie utknąłem, proszę o pomoc!Zły CRLF w strumieniu UTF-16?

Dla normalnego trybu „ASCII” Poniższe uproszczone plików i strumień wyjścia

FILE *fa = fopen("utfOutFA.txt", "w"); 
fprintf(fa, "Line1\nLine2"); 
fclose(fa); 
ofstream sa("utfOutSA.txt"); 
sa << "Line1\nLine2"; 
sa.close(); 

wynik, oczywiście, dokładnie w tych samych plikach tekstowych (hex zrzutu):

00000000h: 4C 69 6E 65 31 0D 0A 4C 69 6E 65 32    ; Line1..Line2 

gdzie nowy linia \n jest rozszerzana do CRLF: 0D 0A - typowa dla systemu Windows.

Teraz robimy to samo dla wyjścia Unicode, a konkretnie UTF-16 LE, który jest rodzajem "domyślnym". Plik wyjściowy

FILE *fu = fopen("utfOutFU.txt", "w, ccs=UNICODE"); 
fwprintf(fu, L"Line1\nLine2"); 
fclose(fu); 

wyniki w tej treści:

00000000h: FF FE 4C 00 69 00 6E 00 65 00 31 00 0D 00 0A 00 ; ÿþL.i.n.e.1..... 
00000010h: 4C 00 69 00 6E 00 65 00 32 00     ; L.i.n.e.2. 

która wygląda idealnie poprawne rozważa BOM i bajt, włączając CRLF: 0D 00 0A 00. Jednakże, podobnie wyjściowy strumień

wofstream su("utfOutSU.txt"); 
su.imbue(locale(locale::empty(), new codecvt_utf16<wchar_t, 0x10ffffUL, 
          codecvt_mode(generate_header + little_endian)>)); 
su << L"Line1\nLine2"; 
su.close(); 

wyniki w jeden bajt mniej i ogólnie nieprawidłowy plik tekstowy:

00000000h: FF FE 4C 00 69 00 6E 00 65 00 31 00 0D 0A 00 4C ; ÿþL.i.n.e.1....L 
00000010h: 00 69 00 6E 00 65 00 32 00      ; .i.n.e.2. 

Powodem jest źle ekspansja CRLF: 0D 0A 00. Czy to błąd? Czy zrobiłem coś złego?

Używam kompilatora Microsoft Visual Studio (14.0 i inne). Próbowałem użyć strumienia endl zamiast \n - ten sam wynik! Próbowałem umieścić najpierw su.imbue(), a następnie su.open() - wszystko to samo! Ja również sprawdzone wyjście UTF-8 (ccs=UTF-8 do pliku i codecvt_utf8 dla Stream) - nie ma problemu jak CRLF pozostaje taka sama jak w trybie ASCII: 0D 0A

I wdzięczni za wszelkie pomysły i uwagi dotyczące tej kwestii.

+0

Jest to prawdopodobnie błąd, ponieważ koder UTF-16 nie może mieć nieparzystej liczby bajtów. – CAF

Odpowiedz

2

Gdy jesteś imbue() "zmieniając lokalizację na, usuwasz oryginalne ustawienia narodowe. Nie używaj wartości locale::empty(), zamiast tego użyj su.getloc(), aby nowe ustawienia lokalne kopiowały stare ustawienia regionalne przed ich modyfikacją.

Ponadto, na marginesie, ostatni parametr szablonu codecvt_utf16 jest maską bitową, więc codecvt_mode(generate_header + little_endian) naprawdę powinien być std::generate_header | std::little_endian.

su.imbue(std::locale(su.getloc(), new codecvt_utf16<wchar_t, 0x10ffffUL, 
          std::generate_header | std::little_endian>)); 
+1

Nie ma różnicy między '+' i '|', gdy bity w każdym operandzie nie nakładają się, ale twój punkt jest dobry. –

+0

Dzięki za pokazanie mi 'getloc()' - Jestem całkiem początkującym w tej dziedzinie. – lariona

+0

Co do trybu kodeków, miałem '|' na wczesnym etapie; następnie zmieniono go na '+' podczas próby kompilatora 'błąd C2440: 'specjalizacja': nie można konwertować z 'int' na 'std :: codecvt_mode''; w końcu musiała dodać nie rzucającą się w oczy obsadę, ale zaniedbała zmianę z powrotem na '|'. Dzięki i tak. – lariona