This was my approach:
#include <iostream>
#include <locale>
#include <string>
int
main()
{
setlocale(LC_ALL, "es_ES.UTF-8"); // This is my system's
locale.
std::locale loc = std::cout.getloc();
std::string str = "cigüeñal";
for (std::string::const_iterator i = str.begin(); i < str.end
(); ++i)
std::cout << std::toupper(*i, loc); // toupper
function from <locale> header
std::cout << std::endl;
return 0;
}
But the output is `CIGüEñAL' instead of `CIGÜEÑAL'. What am I doing
wrong?
Thanks,
Quique
Note: Using such strings in C++ sources is not generally okay, I think it
depends on the actual platform whether it is supported or not. Also, the
question arises which encoding this source file is in!
> for (std::string::const_iterator i = str.begin(); i < str.end(); ++i)
> std::cout << std::toupper(*i, loc);
> // toupper function from <locale> header
One little nitpick: don't compare with less-than. That only works with few
iterators while comparison for inequality always works.
> But the output is `CIG�E�AL' instead of `CIG�E�AL'. What am I doing
> wrong?
Actually, I'm afraid that this is a bug in your locale implementation. The
point is that it recognizes the letters from the basic character set and
handles them correctly while it mishandles the accented ones.
Try this:
std::wstring wstr = L"cig�e�al";
... // replace std::string with std::wstring
The advantage is that wchar_t-based strings can typically hold the whole
Unicode character set, not only a limited subset thereof. For that reason
they are often the preferred choice and I would say their support is more
mature. Also, there is a standard way to encode Unicode characters into
string literals using the basic character set only (e.g. "\u20ac") so that
the problem with how to code such texts doesn't occur.
Uli
> Quique wrote:
>> I'm trying to convert a string in Spanish to uppercase. Spanish, as
>> most European languages, makes use of several accented letters
>> (�������).
>> I thought the toupper function from the <locale> header would do the
>> trick, but it doesn't seem to work.
> [...]
>> setlocale(LC_ALL, "es_ES.UTF-8"); // This is my system's locale.
>> std::locale loc = std::cout.getloc();
>>
>> std::string str = "cig�e�al";
>
> Note: Using such strings in C++ sources is not generally okay, I think
> it depends on the actual platform whether it is supported or not.
> Also, the question arises which encoding this source file is in!
My guess is that the source file is stored in UTF-8 encoding.
If that is the case, then the accented characters will be stored as two
bytes each. You can verify this by opening the source file in a hex
editor.
>
>> for (std::string::const_iterator i = str.begin(); i < str.end(); ++i)
>> std::cout << std::toupper(*i, loc);
>> // toupper function from <locale> header
>
>
> One little nitpick: don't compare with less-than. That only works with
> few iterators while comparison for inequality always works.
>
>> But the output is `CIG�E�AL' instead of `CIG�E�AL'. What am I doing
>> wrong?
>
> Actually, I'm afraid that this is a bug in your locale implementation.
> The point is that it recognizes the letters from the basic character
> set and handles them correctly while it mishandles the accented ones.
I am not sure that this is actually a bug in the locale implementation.
The accented characters in the test string correspond respectively the
Unicode code points U00fc and U00f1. When encoded in UTF-8, these code-
points require multiple bytes. In the loop for upper-casing the string,
each of the bytes making up the code-point is passed individually to
std::toupper(). As those bytes do not represent lower-case letters,
toupper() returns them unchanged.
It is then the console that recognises two consecutive bytes making up a
Unicode code-point and displays the corresponding glyph.
As toupper never sees the accented character, it can not replace it with
an upper-case version.
>
> Try this:
>
> std::wstring wstr = L"cig�e�al";
> ... // replace std::string with std::wstring
>
> The advantage is that wchar_t-based strings can typically hold the
> whole Unicode character set, not only a limited subset thereof. For
> that reason they are often the preferred choice and I would say their
> support is more mature.
Yes, for manipulating the characters in a string, std::wstring is far
better suited if you want to be able to handle characters outside the
basic character set.
> Also, there is a standard way to encode
> Unicode characters into string literals using the basic character set
> only (e.g. "\u20ac") so that the problem with how to code such texts
> doesn't occur.
>
> Uli
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
> > One little nitpick: don't compare with less-than. That only works with
> > few iterators while comparison for inequality always works.
Thank you, I wasn't aware of that.
> > Try this:
>
> > std::wstring wstr = L"cigüeñal";
> > ... // replace std::string with std::wstring
I also had to use towupper() rather than toupper(). However, that
printed the character numbers rather than the characters themselves.
Finally, I found wcout, and with a cast from wint_t to wchar_t, now it
seems to work.
This is the (hopefully correct) code I ended up with:
#include <iostream>
#include <locale>
#include <string>
int
main()
{
std::locale loc(""); // Construct locale object with the
system's setting (es_ES.UTF-8).
std::locale::global(loc);
std::wcout.imbue(loc); // Imbue that locale in wcout.
// std::wcout << L"Current locale is: \'" << loc.name().c_str()
<< L"\'\n";
// std::wcout << L"wcout locale is \'" << std::wcout.getloc().name
().c_str() << L"\'\n";
std::wstring wstr = L"cig\u00fce\u00f1al";
// transform(wstr.begin(), wstr.end(),
std::ostream_iterator<wchar_t, wchar_t>(std::wcout), std::towupper);
for (std::wstring::iterator i = wstr.begin(); i != wstr.end();
++i)
std::wcout << (wchar_t) std::towupper(*i);
std::wcout << std::endl;
return 0;
}
Any comment will be welcome.