Search code examples
cunicode

Problem using wscanf in c for wide characters


I am trying to enter 3 characters, one of which is å. Here is the code:

#ifdef _MSC_VER
#include <io.h>     // _setmode
#include <fcntl.h>  // _O_U16TEXT
#endif

#include <stdio.h>
#include <locale.h>
#include <wchar.h>
#define SIZE 4

void set_locale_mode() {
   #ifdef _MSC_VER
   // Unicode UTF-16, little endian byte order (BMP of ISO 10646)
   const char *CP_UTF_16LE = ".1200";

   setlocale(LC_ALL, CP_UTF_16LE);
   _setmode(_fileno(stdout), _O_U16TEXT);
   #else
      setlocale(LC_ALL, "");
   #endif
}


int main(void) {

   set_locale_mode(); 
   wchar_t myString[SIZE];
   wchar_t testChar=0x00E5;

   wprintf(L"Your test character is %lc\n", testChar);
   printf("Now, enter 3 characters: ");
   wscanf(L"%ls", myString);
   wprintf(L"Your input is %ls\n", myString);

   return 0;
}

However when I enter for example blå I get this output:

You test character is å
Now, enter 3 characters: blå 
Your input is bl┼

Why does not å get printed correctly after I use wscanf?

I am on windows.


Solution

  • Since you want input to be using _O_U16TEXT too, add that to the set_locale_mode function.

    I also suggest not mixing "wide" output with narrow, like wprintf and printf.

    #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
    // perhaps there is already a macro for this, but I don't know of one:
    #define ON_WINDOWS
    #endif
    
    #ifdef ON_WINDOWS
    #define _CRT_SECURE_NO_WARNINGS
    #include <io.h>     // _setmode
    #include <fcntl.h>  // _O_U16TEXT
    
    // just in case mingw doesn't define it after all:
    #ifndef _O_U16TEXT
    #define _O_U16TEXT (0x20000)
    #endif
    #endif
    
    #include <stdio.h>
    #include <locale.h>
    #include <wchar.h>
    #define SIZE 4
    
    void set_locale_mode() {
    #ifdef ON_WINDOWS
        // Unicode UTF-16, little endian byte order (BMP of ISO 10646)
        const char* CP_UTF_16LE = ".1200";
    
        setlocale(LC_ALL, CP_UTF_16LE);
        _setmode(_fileno(stdin), _O_U16TEXT);                // <- Added
        _setmode(_fileno(stdout), _O_U16TEXT);
    #else
        setlocale(LC_ALL, "");
    #endif
    }
    
    
    int main(void) {
        set_locale_mode();
    
        wchar_t myString[SIZE];
        wchar_t testChar = u'\u00E5'; // shows intent clearer than 0x00E5
    
        wprintf(L"Your test character is %lc\n", testChar);
        wprintf(L"Now, enter 3 characters: ");              // <- Use wprintf here
        wscanf(L"%3ls", myString); // <- limit to 3 since SIZE is 4
        wprintf(L"Your input is %ls\n", myString);
    }