Odczyt z pliku UTF-8

0
#ifndef UNICODE
#define UNICODE
#endif

#include <Windows.h>
#include <cstdio>
#include <fstream>

using namespace std;

int main()
{
	FILE* resFile;
	char multiByteStr[256];
	ifstream oFile;
	FILE* exampleFile;
	TCHAR buffer[256];
	
	resFile = _wfopen(L"foo",L"w, ccs=UTF-8");

	system("chcp 65001");

	fwprintf(resFile,L"%s",L"C:\\istniejacyfolder\\zażółć gęśłą jaźń ☺☻♥♦• ć.txt");

	fclose(resFile);

	
	
	oFile.open(L"foo");

	oFile.getline(multiByteStr,256,'\n');
	
	oFile.close();

	MultiByteToWideChar(CP_UTF8,0,multiByteStr,-1,buffer,256);

	wprintf(L"%ls",buffer);

	

	exampleFile = _wfopen(buffer,L"w, ccs=UTF-16LE"); //*
	
	fwprintf(exampleFile,L"%ls",buffer);

	fclose(exampleFile);

	system("pause");
	return 0;
}

W tym wypadku _wfopen zwraca NULL i działanie programu nic nie wnosi.

user image

Przy następnym breakpoincie wyskakuje messageBox "Debug assertion failed".

Co śmieszne, gdy przepiszę ręcznie bufor do funkcji otwierającej plik, tzn.

 exampleFile = _wfopen(L"C:\\istniejacy folder\\zażółć gęśłą jaźń ☺☻♥♦• ć.txt",L"w, ccs=UTF-16LE"); 

plik tworzy się w prawidłowym miejscu, a wszystkie unikodowe znaki są zachowane. Dlaczego tak się dzieje?

1

Gdy czytasz treść pliku, na początku możesz napotkać znak U+FEFF (ZERO WIDTH NO-BREAK SPACE). Gdy wczytujesz linię, która potem jest używana jako nazwa pliku, trzeba się go pozbyć.

wchar_t *buffer_no_bom;
if (buffer[0] == L'\xFEFF')
    buffer_no_bom = &buffer[1];
else
    buffer_no_bom = &buffer[0];

wprintf(L"#%ls#\n",buffer_no_bom);
exampleFile = _wfopen(buffer_no_bom,L"w, ccs=UTF-16LE"); //*
0

Ano faktycznie :) Dziwna sprawa, plik "foo" zakodował się w UTF-8, a nie UTF-8 without bom . Ale po kiego grzyba BOM w UTF-8? Zawsze myślałem, że w tym kodowaniu kolejność bitów jest jednoznaczna...

1

Głównie do tego, by po sekwencji bajtów EF BB BF łatwo rozpoznać że to właśnie UTF-8. U ciebie fopen() właśnie to robi, dlatego nie musisz przy otwieraniu podawać ccs=.

0

Widzę, że okropnie przemieszałeś biblioteki standardowe C, C++ i API Windowsa. Docelowo w C++ obsługa Unicode ma być łatwa i przyjemna, ale póki co jeszcze nie jest. Możliwe do ustawienia locale w runtime biblioteki standardowej C++ pośród swoich "właściwości" (facets) ma codecvt opisujące konwertowanie pomiędzy kodowaniami i właśnie z tego korzystają strumienie plikowe. W C++11 pojawiło się w standardzie m.in. std::codecvt_utf8.

I tak, przykładowo zapisanie i odczytanie jakiegoś napisu mogłoby wyglądać tak:

locale::global( locale(locale(""), new std::codecvt_utf8<wchar_t>() )); // ustawienie locale domyślnego ze zmienionym `codecvt`

wstring file_name;

{
	wofstream file_output("foo");

	file_output << L"C:\\zażółć gęśłą jaźń ☺☻♥♦• ć.txt";
}

{
	wifstream file_input("foo");
		
	getline(file_input, file_name);
}

W Visual Studio od wersji 2010 nawet to zadziała, ale nawet w GCC 4.7.0 nagłówka codecvt po prostu nie ma i trzeba ratować się #ifdefami, itd..

1 użytkowników online, w tym zalogowanych: 0, gości: 1