0

I want to use a plain text file to save input from a user to read it out later In the application it is possible that there occur German and Spanish special signs. Unfortunately the code below doesn't save the signs properly in the file. How is it best to solve this issue?

I solved this issue in a similar application in C by saving in a .bin file instead of a .txt but isnt there a better solution in c++?

#include <string>
#include <fstream>
#include <iostream>

int main() {


    std::string s = "abcöäüÑ"; //(alt + 165 for Ñ)
    std::ofstream ofs{ "test.txt" };
    ofs << s <<'\n';            //this works fine  "abcöäüÑ"


    std::string s2;
    std::cin >> s2;     //typeing in the app abcöäüÑ   (alt+165 for Ñ)
    // i use a windows system with a german keyboard setting

    ofs << s2 <<'\n';       //this doesn't it gets   "abc”„¥"
}

I use a windows 7 64 system with visual studio 2017 with a german keyboard setting.

Mgetz
  • 5,108
  • 2
  • 33
  • 51
Sandro4912
  • 313
  • 1
  • 9
  • 29

1 Answers1

0

The easiest solution (which is deprecated) is to use ANSI code page for German:

setlocale(LC_ALL, "gr");
cout << "abcöäüÑ\n";

ofstream fout("ansi.txt");
fout << "abcöäüÑ\n";

This will work for some limited character sets, it's relatively safe if you stick to Western Latin characters. Maybe this is what you have done with your C code. It doesn't have much to do with saving the file in binary or text.

In Windows it is recommended to use Unicode with wide string functions. Example:

#include <iostream>
#include <string>
#include <fstream>
#include <codecvt>
#include <io.h>
#include <fcntl.h>

int main() 
{
    _setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout
    _setmode(_fileno(stdin), _O_U16TEXT); //wcin  instead of cin
    std::locale loc_utf16(std::locale(), new std::codecvt_utf16<wchar_t>);

    std::wofstream fout(L"utf16.txt", std::ios::binary);
    if(fout)
    {
        fout.imbue(loc_utf16);
        fout << L'\xFEFF'; //insert optional BOM for UTF16
        fout << L"abcöäüÑ ελληνική\r\n";
        fout.close();
    }

    std::wifstream fin(L"utf16.txt", std::ios::binary);
    if(fin)
    {
        fin.imbue(loc_utf16);
        fin.seekg(2, std::ios::beg); //skip optional BOM if it were added
        std::wstring ws;
        while(std::getline(fin, ws)) std::wcout << ws << std::endl;
        fin.close();
    }
    return 0;
}

The disadvantage with UTF16 is that programs on systems like Linux may have a hard time with this format. Some people will save the file in UTF8 format, because UTF8 is more familiar with other systems.

Windows itself is UTF16 based. So you have to read and display the input in UTF16. But you can read/write the file in UTF8 format. Example:

std::wcout << "enter:";
std::wstring sw;
std::wcin >> sw;

std::locale loc_utf8(std::locale(), new std::codecvt_utf8<wchar_t>);
std::wofstream fout(L"utf8.txt", std::ios::binary);
if(fout)
{
    fout.imbue(loc_utf8);
    fout << sw << L"\r\n";
    fout << L"abcöäüÑ ελληνική\r\n";
}

Here the binary flag does matter.

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • can you explain why you have to use `_setmode(_fileno(stdout), _O_U16TEXT);//wcout instead of cout _setmode(_fileno(stdin), _O_U16TEXT); //wcin instead of cin auto loc = std::locale(std::locale(), new std::codecvt_utf16());` why it doesnt work by just use std::wstring, std::wcout ?? – Sandro4912 Apr 19 '18 at 17:54
  • I don't know the inner workings of the console, and I don't think you are interested in that explanation. You might ask *"why is that not done by default?"* The reason is you cannot use `std::cout` after calling `_setmode` (unless you reset the mode back to `_O_TEXT`) somebody decided not to break `cout` for default programs. `wfstream` is a little complicated but that's because it can support both UTF8 and UTF16. See updated answer. – Barmak Shemirani Apr 19 '18 at 18:38