Można podejść tak: Każdy plik jest ciągiem bajtowym, więc można odczytać całą zawartość do listy lub tablicy char
, a potem zamienić na tekst już jakimś algorytmem. Oczywiście, analogiczną zmianę można wykonać w drugą stronę.
Ja sam miałem dość podobny problem, czyli odczytywanie i zapisywanie tekstu w różnych kodowaniach i podam takiego gotowca:
https://github.com/andrzejlisek/TextPaintWeb/blob/main/prog/textcodec.h
https://github.com/andrzejlisek/TextPaintWeb/blob/main/prog/textcodec.cpp
Przykład użycia obiektu typu TextCodec:
// Zapis pliku:
Codec.get()->Reset();
Codec.get()->AddBOM();
Codec.get()->EnqueueStr(Text);
Codec.get()->DequeueRaw(BinaryData);
// Odczyt pliku:
Codec.get()->Reset();
Codec.get()->EnqueueRaw(BinaryData);
Codec.get()->RemoveBOM();
Codec.get()->DequeueStr(Text);
Jest to moja implementacja różnych kodeków, między innymi UTF-8, która może się przydać.
Idea jest taka, że obiekt konstruuje się numerem kodeka, numery są takie same, jak TextEncoding w C#. Sam kodek działa tak, że w każdej chwili można dopisać dane jako string EnqueueStr
, wewnątrz zamienią się na dane surowe i można wyciągnąć jako dane surowe EnqueueRaw
. Implementację zamiana danych surowych na tekstowe jest w funkcji DequeueStr
. Przy tej implementacji można konwertować tekst fragmentami, nie potrzeba wczytywać od razu całego pliku do kodeka i z niego wyciągać. Kodek też pamięta stan konwersji, bo np. w UTF-8 jeden znak może zajmować 2 lub 3 bajty.
Inaczej mówiąc, można powiedzieć, że kodek działa na zasadzie strumieni, że wchodzi strumień bajtowy i wychodzi strumień tekstowy lub wchodzi strumień tekstowy i wychodzi strumień bajtowy. W przypadku konwersji z UTF-8/UTF-16 na tekst, kodek uwzględnia przypadek końca danych "w połowie znaku", czyli jest pierwszy bajt wielobajtowego znaku i na nim strumień się kończy. W takim przypadku, nie można z kodeka wyciągnąć znaku tekstowego (nic nie zostanie dopisane do strumienia tekstowego), dopóki do ciągu binarnego nie będą dopisane wszystkie bajty składowe znaku.
Możesz przejrzeć cały projekt, nie widze sensu wstawiać implementacji typów Raw
i Str
, są to odpowiednio tablica danych surowych i odpowiednik zwykłego String. Własne struktury w tym przypadku wynikają z tego, że ja ten program przerabiałem z C# i chciałem mieć interfejs standardowych struktur podobny do tego w C#, a potem dopisywałem dodatkowe funkcje w miare potrzeb.
Myślę, że bez większego trudu można przerobić funkcje kodeka tak, żeby wchodziły do nich standardowe struktury, jak np std::vector
do danych surowych i std::string
do tekstu.
Jest jeszcze inny problem: Typ std::string
przeznacza jeden bajt na tekst i w zależności od kompilatora może domyślnie interpretować jako UTF-8 przy cin
i cout
, jestdnak trzeba to mieć na uwadze. Jest to jeszcze jeden powód, dla którego utworzyłem customowy typ Str
, który tak naprawdę jest wektorem liczb i nie ma problemu z dziwnymi znakami. Pierwotnie, w C# też posługiwałem się listą liczb, bo w tym przypadku były jakieś kłopoty przy "egzotycznych" znakach.