Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Convert accented letters to uppercase

201 views
Skip to first unread message

Quique

unread,
Oct 8, 2009, 7:33:14 AM10/8/09
to
Hi,
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.

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

Ulrich Eckhardt

unread,
Oct 8, 2009, 11:24:38 AM10/8/09
to
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!

> 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

Bart van Ingen Schenau

unread,
Oct 8, 2009, 1:05:35 PM10/8/09
to
Ulrich Eckhardt wrote:

> 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/

Quique

unread,
Oct 14, 2009, 9:00:23 AM10/14/09
to
On Oct 8, 7:05 pm, Bart van Ingen Schenau wrote:

> > 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.

0 new messages