我在使用 C++ 中的 tolower() 函数将字符串转换为小写时遇到问题。对于普通字符串,它可以按预期工作,但是特殊字符无法成功转换。
我如何使用我的功能:
string NotLowerCase = "Grüßen";
string LowerCase = "";
for (unsigned int i = 0; i < NotLowerCase.length(); i++) {
LowerCase += tolower(NotLowerCase[i]);
}
例如:
3 和 4 没有按预期工作,如您所见
如何解决这个问题?我必须保留特殊字符,但要保留小写。
tolower
的示例代码(如下)显示了如何解决此问题;您必须使用默认“C”语言环境之外的其他语言。
#include <iostream>
#include <cctype>
#include <clocale>
int main()
{
unsigned char c = '\xb4'; // the character Ž in ISO-8859-15
// but ´ (acute accent) in ISO-8859-1
std::setlocale(LC_ALL, "en_US.iso88591");
std::cout << std::hex << std::showbase;
std::cout << "in iso8859-1, tolower('0xb4') gives "
<< std::tolower(c) << '\n';
std::setlocale(LC_ALL, "en_US.iso885915");
std::cout << "in iso8859-15, tolower('0xb4') gives "
<< std::tolower(c) << '\n';
}
您还可以将
std::string
更改为 std::wstring
,这是许多 C++ 实现上的 Unicode。
wstring NotLowerCase = L"Grüßen";
wstring LowerCase;
for (auto&& ch : NotLowerCase) {
LowerCase += towlower(ch);
}
Microsoft 的指导是“将字符串标准化为大写”,因此您可以使用
toupper
或 towupper
来代替。
请记住,逐个字符的转换可能不适用于某些语言。例如,使用德国所说的德语,将 Grüßen 全部大写,就会变成 GRÜESSEN(尽管现在有一个大写的 ẞ)。还有许多其他“问题”,例如组合字符;如果您正在使用字符串进行真正的“生产”工作,您确实需要一种完全不同的方法。
最后,C++ 对管理区域设置有更复杂的支持,请参阅 了解详细信息。用户选择的区域设置,这是通过将区域设置设置为""
(空字符串)来实现的。
std::locale::global(std::locale(""));
这会将语言环境设置为程序运行时所使用的区域设置,并影响在多字节和宽字符串字符之间进行转换的标准字符转换例程(
std::mbsrtowcs & std::wcsrtombs)。
然后,您可以使用这些函数将系统/用户选择的多字节字符(例如UTF-8
)转换为系统标准宽字符代码,这些代码可用于一次操作一个字符的函数(例如
std::tolower
) .这很重要,因为像
UTF-8
这样的多字节字符集无法使用像
std::tolower()
这样的单字符操作进行转换。将宽字符串版本转换为大写/小写后,可以将其转换回系统/用户多字节字符集以打印到控制台。
// Convert from multi-byte codes to wide string codes
std::wstring mb_to_ws(std::string const& mb)
{
std::wstring ws;
std::mbstate_t ps{};
char const* src = mb.data();
std::size_t len = 1 + mbsrtowcs(0, &src, 3, &ps);
ws.resize(len);
src = mb.data();
mbsrtowcs(&ws[0], &src, ws.size(), &ps);
if(src)
throw std::runtime_error("invalid multibyte character after: '"
+ std::string(mb.data(), src) + "'");
ws.pop_back();
return ws;
}
// Convert from wide string codes to multi-byte codes
std::string ws_to_mb(std::wstring const& ws)
{
std::string mb;
std::mbstate_t ps{};
wchar_t const* src = ws.data();
std::size_t len = 1 + wcsrtombs(0, &src, 0, &ps);
mb.resize(len);
src = ws.data();
wcsrtombs(&mb[0], &src, mb.size(), &ps);
if(src)
throw std::runtime_error("invalid wide character");
mb.pop_back();
return mb;
}
int main()
{
// set locale to the one chosen by the user
// (or the one set by the system default)
std::locale::global(std::locale(""));
try
{
string NotLowerCase = "Grüßen";
std::cout << NotLowerCase << '\n';
// convert system/user multibyte character codes
// to wide string versions
std::wstring ws1 = mb_to_ws(NotLowerCase);
std::wstring ws2;
for(unsigned int i = 0; i < ws1.length(); i++) {
// use the system/user locale
ws2 += std::tolower(ws1[i], std::locale(""));
}
// convert wide string character codes back
// to system/user multibyte versions
string LowerCase = ws_to_mb(ws2);
std::cout << LowerCase << '\n';
}
catch(std::exception const& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
catch(...)
{
std::cerr << "Unknown exception." << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
代码未经严格测试
wchar_t unicode_tolower(wchar_t c) {
#define LWR_OFFSET(from, to, by){if(c>=(from) && c<=(to)){return c+(by);}}
#define LWR_NEXT(from, to){const int odd = (from) & 0x0001; if(c>=(from) && c<=(to) && ((c&0x0001) == odd)) {return ++c;}}
LWR_OFFSET(L'A', L'Z', 0x20)
LWR_OFFSET(0x00c0, 0x00d6, 0x20) // A with grave ... O with diaeresis
// 0x00d7=multiplication
LWR_OFFSET(0x00d8, 0x00de, 0x20) // O with stroke ...Thorn
LWR_NEXT(0x0100, 0x017e) // A with macron ... Z with caron
LWR_NEXT(0x0370, 0x0376) // greek
LWR_OFFSET(0x0391, 0x03ab, 0x20) // greek
if (c==0x03f7) {return ++c;}
if (c==0x03fa) {return ++c;}
LWR_OFFSET(0x0400, 0x040f, 0x50) // Cyrillic - this range is strange
LWR_OFFSET(0x0410, 0x042f, 0x20) // Cyrillic
LWR_NEXT(0x0460, 0x04bf)
LWR_NEXT(0x04c1, 0x04ce)
LWR_NEXT(0x04d0, 0x052f)
LWR_OFFSET(0x0531, 0x0556, 0x30) // Armenian
return c;
#undef LWR_OFFSET
#undef LWR_NEXT
}
string NotLowerCase = "Grüßen";
string LowerCase = "";
for (unsigned int i = 0; i < NotLowerCase.length(); i++) {
if(NotLowerCase[i]<65||NotLowerCase[i]>122)
{
LowerCase+='?';
}
else
LowerCase += tolower(NotLowerCase[i]);
}