How to print euro sign from c console app using gnome terminal

100 views Asked by At

I am trying to print the euro sign on the console in the Gnome Terminal app through a c console application. My code is:

#include <stdio.h>
#include <wctype.h>
#include <wchar.h>
#include <locale.h>

int main (int argc, char* argv[]) {
    setlocale(LC_CTYPE,"UTF-8");
    wchar_t w_char1 = '€';
    wprintf(L"%x\n", w_char1);
    wprintf(L"%c\n", w_char1);
    wchar_t w_char2=0xE282AC;
    wprintf(L"%x\n", w_char2);
    wprintf(L"%c\n", w_char2);
    wchar_t w_char3='\u20AC';
    wprintf(L"%x\n", w_char3);
    wprintf(L"%c\n", w_char3);
}

This prints the following on the console:

e282ac

e282ac

e282ac

I've tried it both with and without setlocale. In the Gnome Terminal Preferences, under Compatibility, the Encoding is set as Unicode -- UTF-8.

Any ideas why the Euro symbol does not print?

1

There are 1 answers

0
Jonathan Leffler On

This does not have a complete solution, but I think it provides some useful pointers towards a solution. It being midnight here, I need to go to bed.

The POSIX manual page for wprintf() indicates that you need to use an l modifier before the c conversion specifier:

l (ell)
Specifies that a following d, i, o, u, x, or X conversion specifier applies to a long or unsigned long argument; that a following n conversion specifier applies to a pointer to a long argument; that a following c conversion specifier applies to a wint_t argument; that a following s conversion specifier applies to a pointer to a wchar_t argument; or has no effect on a following a, A, e, E, f, F, g, or G conversion specifier.

There's then a question of whether a wchar_t converts to a wint_t — it probably does.

I ran into a problem that I had to compile with -Wno-multichar to suppress errors like:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -fno-common wprintf37.c -o wprintf37
wprintf37.c: In function ‘main’:
wprintf37.c:9:23: error: multi-character character constant [-Werror=multichar]
    9 |     wchar_t w_char1 = '€';
      |                       ^~~
wprintf37.c:15:21: error: multi-character character constant [-Werror=multichar]
   15 |     wchar_t w_char3='\u20AC';
      |                     ^~~~~~~~
cc1: all warnings being treated as errors
$

You also should check whether the setlocale() operation works — it doesn't for me. I used setlocale(LC_ALL, "") and checked it; that worked. I am using a Mac, and have LANG=en_us.UTF-8 set in the environment. And I'm still not seeing the Euro symbol.

Hmmm…

One of the rules of debugging when things are failing is "check every function that can report an error". This code does that, and the result is as shown:

#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>

static void err_warn(int rv, const char *msg)
{
    fwprintf(stderr, L"%s: rv = %d (%d) %s\n", msg, rv, errno, strerror(errno));
    errno = 0;
    clearerr(stdout);
}

int main(void)
{
    if (setlocale(LC_ALL, "") == NULL)
    {
        fprintf(stderr, "failed to set the default locale\n");
        return 1;
    }
    int rv;
    wchar_t w_char1 = '€';
    if ((rv = wprintf(L"%x\n", w_char1)) < 0)
        err_warn(rv, "wprintf - 1");
    if ((rv = wprintf(L"C1 = %lc\n", w_char1)) < 0)
        err_warn(rv, "wprintf - 2");
    putwchar(L'\n');
    fflush(stdout);

    wchar_t w_char2=0xE282AC;
    if ((rv = wprintf(L"%x\n", w_char2)) < 0)
        err_warn(rv, "wprintf - 3");
    if ((rv = wprintf(L"C2 = %lc\n", w_char2)) < 0)
        err_warn(rv, "wprintf - 4");
    putwchar(L'\n');
    fflush(stdout);

    wchar_t w_char3='\u20AC';
    if ((rv = wprintf(L"%x\n", w_char3)) < 0)
        err_warn(rv, "wprintf - 5");
    if ((rv = wprintf(L"C3 = %lc\n", w_char3)) < 0)
        err_warn(rv, "wprintf - 6");
    putwchar(L'\n');
    fflush(stdout);

    char s[] = "€";
    if ((rv = wprintf(L"S = %s\n", s)) < 0)
        err_warn(rv, "wprintf - 7");

    return 0;
}

Output (on my Mac):

e282ac
wprintf - 2: rv = -1 (92) Illegal byte sequence
C1 = 
e282ac
wprintf - 4: rv = -1 (92) Illegal byte sequence
C2 = 
e282ac
wprintf - 6: rv = -1 (92) Illegal byte sequence
C3 = 
S = €

Note that the error reporting function clears the error status on standard output (clearerr(stdout)) and resets errno to zero — no standard C library function does that. Before I called clearerr(), all the operations after the first reported failure.

I think that it may be necessary to use u8'€' or something similar. Check §6.4.4.4 Character constants and §6.4.5 String literals for more ideas.


Twenty-four hours later.

This code is working moderately well:

#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>

static void err_warn(int rv, const char *msg)
{
    fwprintf(stderr, L"%s: rv = %d (%d) %s\n", msg, rv, errno, strerror(errno));
    errno = 0;
    clearerr(stdout);
}

int main(void)
{
    if (setlocale(LC_ALL, "") == NULL)
    {
        fprintf(stderr, "failed to set the default locale\n");
        return 1;
    }
    int rv;

    /* This now works */
    wchar_t w_char1 = L'€';
    if ((rv = wprintf(L"%x\n", w_char1)) < 0)
        err_warn(rv, "wprintf - 1");
    if ((rv = wprintf(L"C1 = %lc\n", w_char1)) < 0)
        err_warn(rv, "wprintf - 2");
    putwchar(L'\n');
    fflush(stdout);

    /* Still failing */
    wchar_t w_char2 = 0xE282AC;
    if ((rv = wprintf(L"%x\n", w_char2)) < 0)
        err_warn(rv, "wprintf - 3");
    if ((rv = wprintf(L"C2 = %lc\n", w_char2)) < 0)
        err_warn(rv, "wprintf - 4");
    putwchar(L'\n');
    fflush(stdout);

    /* This works */
    wchar_t w_char3 = 0x20AC;
    if ((rv = wprintf(L"%x\n", w_char3)) < 0)
        err_warn(rv, "wprintf - 5");
    if ((rv = wprintf(L"C2 = %lc\n", w_char3)) < 0)
        err_warn(rv, "wprintf - 6");
    putwchar(L'\n');
    fflush(stdout);

    /* This now works */
    wchar_t w_char4 = L'\u20AC';
    if ((rv = wprintf(L"%x\n", w_char4)) < 0)
        err_warn(rv, "wprintf - 7");
    if ((rv = wprintf(L"C3 = %lc\n", w_char4)) < 0)
        err_warn(rv, "wprintf - 8");
    putwchar(L'\n');
    fflush(stdout);

    char s[] = "€";
    if ((rv = wprintf(L"S = %s\n", s)) < 0)
        err_warn(rv, "wprintf - 9");

    return 0;
}

The output it gives is:

20ac
C1 = €

e282ac
wprintf - 4: rv = -1 (92) Illegal byte sequence
C2 = 
20ac
C2 = €

20ac
C3 = €

S = €

Notice how the successful values all contain the U+20AC.