我有一个字符串,我想大写,但它可能包含波兰语特殊字母(,ć,,∪,ñ,ó,)。函数transform(string.begin(), string.end(), string.begin(), ::toupper);
只将拉丁字母大写,所以我写了一个这样的函数:
string to_upper(string nazwa)
{
transform(nazwa.begin(), nazwa.end(), nazwa.begin(), ::toupper);
for (int i = 0; i < (int)nazwa.size(); i++)
{
switch(nazwa[i])
{
case u'ą':
{
nazwa[i] = u'Ą';
break;
}
case u'ć':
{
nazwa[i] = u'Ć';
break;
}
case u'ę':
{
nazwa[i] = u'Ę';
break;
}
case u'ó':
{
nazwa[i] = u'Ó';
break;
}
case u'ł':
{
nazwa[i] = u'Ł';
break;
}
case u'ń':
{
nazwa[i] = u'Ń';
break;
}
case u'ś':
{
nazwa[i] = u'Ś';
break;
}
case u'ż':
{
nazwa[i] = u'Ż';
break;
}
case u'ź':
{
nazwa[i] = u'Ź';
break;
}
}
}
return nazwa;
}
字符串
我也试过使用if
代替switch
,但它没有改变任何东西。在Qt Creator中,除了u 'Ó'之外,每个要插入的大写字母旁边都会出现类似的错误:Implicit conversion from 'char16_t' to 'std::basic_string<char>::value_type' (aka 'char') changes value from 260 to 4
(来自u'')。运行程序后,字符串中的字符不会交换。
3条答案
按热度按时间mrfwxfqh1#
问题来源
std::string
将字符存储为char
s,长度为一个字节,因此它们的值只能从0到255。这使得不可能将
u'ą'
存储在一个char
中,例如,因为unicode value forą
是0x105
(十进制= 261,高于255)。为了避免这个问题,人们发明了
UTF-8
,这是一种字符编码标准,可以将任何Unicode字符编码为字节。具有更高值的字符当然需要多个字节来编码。您的
std::string
很可能使用UTF-8编码字符。(我说很可能是因为您的代码没有直接指出它,但它几乎是100%肯定的情况下,因为它是唯一的通用方式编码重音字母在char
的字符串。为了100%确定,你需要检查Qt的代码,因为它似乎是你正在使用的)这样做的结果是,你不能只使用一个
for
来迭代你的std::string
的char
s,因为你基本上假设一个char
等于一个字母,这根本不是事实。例如,在
ą
的情况下,它将被编码为字节C4 85
,因此您将有一个值为0xC4
(= 196)的char
,然后是值为0x85
(= 133)的另一个char
。大写字符的具体大小写
幸运的是,Latin Extended-A part of the Unicode table(archive)向我们展示了这些特殊的大写字母正好出现在它们的小写字母之前。
不仅如此,我们还可以看到:
这将使将小写代码点转换为大写代码点变得更容易,因为我们所要做的就是检查字符的索引是否对应于小写,如果是,则将其减去1以使其大写。
用UTF-8编码其中一个字符
用UTF-8编码(源代码):
110xxxxx
,请将xxxxx
替换为字符的二进制码位的高五个字节10xxxxxx
,请将xxxxxx
替换为字符的二进制码位的低六个字节所以对于
ą
,十六进制的值是0x105
,所以二进制的**00100
*000101
*。第一个字节值则为
110
00100
**(= 0xC 4)。第二个字节值则为
10
*000101
*(= 0x 85)。请注意,这种编码“技术”之所以有效,是因为要大写的字符的值(代码点)在0x 80和0x 7 FF之间。它根据值的高低而变化,请参阅此处的文档。
修复代码
我已经重写了你的
to_upper
函数,根据我到目前为止写的内容:字符串
#include <cstdint>
才能使uint16_t
工作。*0x20
得到大写代码点,但这与我在本答案中提到的其他字母的原理几乎相同。我在我的代码中包含了很多注解,请考虑阅读它们以更好地理解。
我已经用字符串
"ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽž"
测试过了,它把它转换成了"ĀĀĂĂĄĄĆĆĈĈĊĊČČĎĎĐĐĒĒĔĔĖĖĘĘĚĚĜĜĞĞĠĠĢĢĤĤĦĦĨĨĪĪĬĬĮĮİİIJIJĴĴĶĶĸĹĹĻĻĽĽĿĿŁŁŃŃŅŅŇŇŊŊŌŌŎŎŐŐŒŒŔŔŖŖŘŘŚŚŜŜŞŞŠŠŢŢŤŤŦŦŨŨŪŪŬŬŮŮŰŰŲŲŴŴŶŶŸŹŹŻŻŽŽ"
,所以它工作得很好:型
x1c 0d1x的数据
chcp 65001
将它们更改为UTF-8,或者在执行代码时使用adding a Windows API call to change the CMD encoding。to_upper
函数版本可以直接使用波兰字母编写代码,而无需进一步修改。当我说一切都使用UTF-8时,我是认真的。string
而不是std::string
,这意味着你的代码中有一个using namespace std;
。在这种情况下,请参见Why is "using namespace std;" considered bad practice? *其他答案备注
请记住,我的回答是非常具体的。它的目的是,如你所要求的,大写波兰字母。
其他答案依赖于
std
的特性,这些特性显然更通用,适用于所有语言,所以我邀请您给予它们。依靠现有的功能总是比重新发明轮子更好,但我认为有一个自制的替代方案也很好,它可能更容易理解,有时更有效。
ccrfmcuu2#
最简单的处理方法是使用宽字符串。唯一的陷阱是正确处理编码/区域设置。
试试这个:
字符串
https://godbolt.org/z/3cKaEeW3z
现在:
cLocale
定义了标准库在与您的程序交互时使用的区域设置。sys
是系统区域设置,它定义了输入输出流应该使用哪种编码。注意使用的是哪个overload toupper。只有当您使用适用于波兰语的单字节编码时,相同的代码才能用于
std::string
和std::cin
std::cout
。在这种情况下,您应该将cLocale
中的字符串更改为:型
请注意,这个区域名称是平台和编译器相关的,并且系统必须配置才能工作。上面的作品在Windows与MSVC(我已经测试)。无法演示,因为没有支持波兰语言环境的在线编译器。
如果使用多字节编码,则转换将失败,因为无法处理此多字节字符
3npbholx3#
这应该可以在大多数Unix-y系统上工作,除了土耳其语I和德语ß等奇怪的情况。
字符串
注意,它使用了不推荐使用的C++工具来进行UTF-8代码转换。如果这让您感到困扰,请替换
stow
和wtos
中的任何UTF-8到UTF-32和反向转换器。您也可以随意替换系统中存在的区域设置(可以是“pl_PL.UTF-8”或类似的)。