如何在C ++中使用大写/小写UTF-8字符?

时间:2016-04-27 18:14:57

标签: c++ string unicode utf-8

让我们假设我有一个包含以下内容的UTF-8编码std::string

óó

我想将其转换为以下内容:

ÓÓ

理想情况下,我希望我使用的大写/小写方法在所有UTF-8中都是通用的。如果那是可能的。

字符串中的原始字节序列是0xc3b3c3b3(每个字符两个字节,以及ó的两个实例),我希望输出为0xc393c393(两个实例) Ó)。 StackOverflow上有some examples但它们使用宽字符串,而other answers表示不应该为UTF-8使用宽字符串。看来这个问题可能非常“棘手”,因为输出可能依赖于用户的语言环境。

我原本期望使用像std::toupper()这样的东西,但我的用法真的不清楚,因为我好像不是一次只转换一个字符而是整个字符串。另外,我将Ideone example放在一起似乎表明toupper()的{​​{1}}只是0xc3b3,这是一个意想不到的结果。将0xc3b3调用UTF-8或ISO8859-1似乎不会改变结果。

如果你能解释我做错了什么或为什么我的问题/前提有缺陷,我会喜欢一些指导!

4 个答案:

答案 0 :(得分:3)

在C ++中没有标准的方法来进行Unicode大小写转换。有一些方法适用于某些 C ++实现,但标准并不要求它们。

如果您想要保证Unicode大小写转换,您将需要使用像ICU或Boost.Locale这样的库(也就是:具有更多类似C ++的接口的ICU)。

答案 1 :(得分:2)

  

StackOverflow上有一些示例,但它们使用宽字符串,而其他答案则表示您不应该使用UTF-8的宽字符串。

(utf8everywhere)中的文章和答案适用于Windows。 C ++标准要求wchar_t足够宽以容纳所有支持的代码单元(32位宽),但与UTF-8完全兼容。在Windows上,wchar_t是UTF-16,但是如果您使用Windows,那么您遇到的问题不仅仅是如果我们要诚实(即他们可怕的API)。

  

这个问题似乎也非常棘手"因为输出可能取决于用户的语言环境。

不是真的。在代码中设置区域设置。如果你没有在shell中设置语言环境,那么像sort这样的某些程序就无法正常工作,所以用户有责任。

  

我原本期望使用像std :: toupper()这样的东西,但我的用法真的不清楚,因为我似乎不是一次只转换一个字符而是整个字符串。

代码示例使用迭代器。如果你不想转换每个角色,请不要。

  

另外,我把这个Ideone的例子放在一起似乎表明0xc3b3的toupper()只是0xc3b3,这是一个意想不到的结果。将setlocale调用为UTF-8或ISO8859-1并不会改变结果。

您有未定义的行为。 unsigned char的范围是255. 0xc3b3方式超过了它。

  

如果你能解释我做错了什么或为什么我的问题/前提有缺陷,我会喜欢一些指导!

这个例子非常合适:

#include <iostream>
#include <string>
#include <locale>

int main()
{
    std::setlocale(LC_CTYPE, "en_US.UTF-8"); // the locale will be the UTF-8 enabled English

    std::wstring str = L"óó";

    std::wcout << str << std::endl;

    for (std::wstring::iterator it = str.begin(); it != str.end(); ++it)
        *it = towupper(*it);

    std::wcout << str << std::endl;
}

输出:ÓÓ

答案 2 :(得分:1)

搜索设备中肯定需要这些不区分大小写的功能。

好吧,我的需求与上述相同,并且在大多数情况下UTF8都很流畅,但是大小写情况并不那么好。看起来完成后不属于待办事项列表吗?因为在过去的这种情况下,它一直是待办事项清单上的主要主题之一。在IBM出厂之前,我已经在修补IBM键盘驱动程序1984,但是有可用的副本。在IBM希望将其投放欧洲之前,还对Displaywrite 1和3(PC-DOS字处理器)进行了修补。在IBM 3270大型机终端系统中的来回国家EBCDIC代码页上做了很多PC-DOS(CP850)和CP1252(Windows)。他们都在待办事项列表上有这个区分大小写的主题。在所有国家ASCII版本和CP1252 Windows表中,都在0x40-0x5F和0x60-0x7F之间进行了切换,以在大小写之间(而不是PCDOS CP850)之间切换0x20。

该怎么办?

tolower()和toupper()在US-ASCII之外的UTF8多字符串中不起作用。它们仅使用一个字节。但是字符串解决方案会起作用,并且还有其他所有解决方案。

西欧人很幸运

好吧,UTF8将CP1252(Windows 8bit / Latin1)作为第一个附加表, Latin-1补码(Unicode块),原样。这意味着可以像常规US ASCII一样移动字母(C3XX)。下面的代码示例。

希腊人,俄罗斯人,冰岛人和东欧人并不那么幸运

对于冰岛人,刚从CP1252中打出了带有中风的Đ/đ-D(与单词the的th音相同)。

可以使用希腊,俄罗斯和东欧的ISO8字符集(CP1253,CP1251和CP1257)(因为直接使用拉丁语CP1252)。那么换档也行得通。但是取而代之的是,有人只是随机地填满了表格(例如PC-DOC 8位ASCII)。

只有一种工作解决方案与PC_DOS ASCII一样,可以使用转换表。我将在下一个X-mas上执行此操作(当我需要它时,它很紧急),但我提示如果有人急着该怎么做。

如何为希腊人,俄罗斯人,冰岛人和东欧人提供解决方案

在编程代码中,针对东欧,希腊文和西里尔字母,制作与UTF8表的不同首字节相关的不同表。在表的UTF8第二字节位置填充字母的第二个字节,将大写字母与匹配的小写第二个字节交换,然后使另一个字母相反。

然后确定与每个表相关的第一个字节。这样,编程代码就可以选择正确的表并只读取正确的位置并获取所需的大写或小写字符。然后,修改下面的字母大小写功能(我为Latin1编写的字母大小写功能),以对某些必须使用表格的第一个UTF8字符使用偏移0x20的表格。它将顺利运行,并且新计算机在内存和电源方面都没有问题。

UTF8字母大小写相关功能Latin1示例

我认为这是可行的,不久之后就尝试了。它仅适用于UTF8的Latin-1和USACII部分。

unsigned char *StrToLwrUft8Latin1(unsigned char *pString)
{
    char cExtChar = 0;
    if (pString && *pString) {
        unsigned char *p = pString;
        while (*p) {
            if (((cExtChar && ((*p >= 0x80) && (*p <= 0xbf)))
                || ((!cExtChar) && (*p <= 0x7f)))
                && ((((*p & 0x7f) + cExtChar) >= 0x40)
                    && (((*p & 0x7f) + cExtChar) <= 0x5f)))
                *p += 0x20;
            if (cExtChar)
                cExtChar = 0;
            else if (*p == 0xc3)
                cExtChar = 0x40;
            p++;
        }
    }
    return pString;
}
unsigned char *StrToUprUft8Latin1(unsigned char *pString)
{
    char cExtChar = 0;
    if (pString && *pString) {
        unsigned char *p = pString;
        while (*p) {
            if (((cExtChar && ((*p >= 0x80) && (*p <= 0xbf)))
                || ((!cExtChar) && (*p <= 0x7f)))
                && ((((*p & 0x7f) + cExtChar) >= 0x60)
                    && (((*p & 0x7f) + cExtChar) <= 0x7e)))
                *p -= 0x20;
            if (cExtChar)
                cExtChar = 0;
            else if (*p == 0xc3)
                cExtChar = 0x40;
            p++;
        }
    }
    return pString;
}
int StrnCiCmpLatin1(const char *s1, const char *s2, size_t ztCount)
{
    unsigned char cExtChar = 0;
    if (s1 && *s1 && s2 && *s2) {
        for (; ztCount--; s1++, s2++) {
            int iDiff = tolower((unsigned char)(*s1 & 0x7f)
                + cExtChar) - tolower((unsigned char)(*s2 & 0x7f) + cExtChar);
            if (iDiff != 0 || !*s1 || !*s2)
                return iDiff;
            if (cExtChar)
                cExtChar = 0;
            else if (((unsigned char )*s2) == ((unsigned char)0xc3))
                cExtChar = 0x40;
        }
    }
    return 0;
}
int StrCiCmpLatin1(const char *s1, const char *s2)
{
    return StrnCiCmpLatin1(s1, s2, (size_t)(-1));
}
char *StrCiStrLatin1(const char *s1, const char *s2)
{
    if (s1 && *s1 && s2 && *s2) {
        char *p = (char *)s1;
        size_t len = strlen(s2);
        while (*p) {
            if (StrnCiCmpLatin1(p, s2, len) == 0)
                return p;
            p++;
        }
    }
    return (0);
}

答案 3 :(得分:1)

此功能涵盖了我发现的UTF8中区分大小写的字符集(可能是匹配中的一些错误,但我认为我没错)。

好吧,这比表格解决方案要短,并且涵盖了所有内容?

可能写得更优雅,但要显示图片。 cmp函数必须复制该操作的字符串,这可能会减慢巨大的字符串并占用内存。但是,我们不再像过去那样共享5KB PDP11的5个人了吗?

这是我想在这样的地方找到的东西。 (不区分大小写的cmp-case不需要大写版本,以后可以添加。)

unsigned char *StrToLwrExt(unsigned char *pString)
{
    if (pString && *pString) {
        unsigned char *p = pString;
        unsigned char *pExtChar = 0;
        while (*p) {
            if ((*p >= 0x41) && (*p <= 0x5a)) // US ASCII
                (*p) += 0x20;
            else if (*p > 0xc0) {
                pExtChar = p;
                p++;
                switch (*pExtChar) {
                case 0xc3: // Latin 1
                    if ((*p >= 0x80)
                        && (*p <= 0x9e)
                        && (*p != 0x97))
                        (*p) += 0x20; // US ASCII shift
                    break;
                case 0xc4: // Latin Exteneded
                    if ((*p >= 0x80)
                        && (*p <= 0xb7)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0xb9)
                        && (*p <= 0xbe)
                        && (*p % 2)) // Odd
                        (*p)++; // Next char is lwr
                    else if (*p == 0xbf) {
                        *pExtChar = 0xc5;
                        (*p) = 0x80;
                    }
                    break;
                case 0xc5: // Latin Exteneded
                    if ((*p >= 0x80)
                        && (*p <= 0x88)
                        && (*p % 2)) // Odd
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0x8a)
                        && (*p <= 0xb7)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0xb9)
                        && (*p <= 0xbe)
                        && (*p % 2)) // Odd
                        (*p)++; // Next char is lwr
                    break;
                case 0xc6: // Latin Exteneded
                    switch (*p) {
                    case 0x82:
                    case 0x84:
                    case 0x87:
                    case 0x8b:
                    case 0x91:
                    case 0x98:
                    case 0xa0:
                    case 0xa2:
                    case 0xa4:
                    case 0xa7:
                    case 0xac:
                    case 0xaf:
                    case 0xb3:
                    case 0xb5:
                    case 0xb8:
                    case 0xbc:
                        (*p)++; // Next char is lwr
                        break;
                    default:
                        break;
                    }
                case 0xc7: // Latin Exteneded
                    if (*p == 0x84)
                        (*p) = 0x86;
                    else if (*p == 0x85)
                        (*p)++; // Next char is lwr
                    else if (*p == 0x87)
                        (*p) = 0x89;
                    else if (*p == 0x88)
                        (*p)++; // Next char is lwr
                    else if (*p == 0x8a)
                        (*p) = 0x8c;
                    else if (*p == 0x8b)
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0x8d)
                        && (*p <= 0x9c)
                        && (*p % 2)) // Odd
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0x9e)
                        && (*p <= 0xaf)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    else if (*p == 0xb1)
                        (*p) = 0xb3;
                    else if (*p == 0xb2)
                        (*p)++; // Next char is lwr
                    else if (*p == 0xb4)
                        (*p)++; // Next char is lwr
                    else if (*p == 0xb8)
                        (*p)++; // Next char is lwr
                    else if (*p == 0xba)
                        (*p)++; // Next char is lwr
                    else if (*p == 0xbc)
                        (*p)++; // Next char is lwr
                    else if (*p == 0xbe)
                        (*p)++; // Next char is lwr
                    break;
                case 0xc8: // Latin Exteneded
                    if ((*p >= 0x80)
                        && (*p <= 0x9f)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0xa2)
                        && (*p <= 0xb3)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    else if (*p == 0xbb)
                        (*p)++; // Next char is lwr
                    break;
                case 0xcd: // Greek & Coptic
                    switch (*p) {
                    case 0xb0:
                    case 0xb2:
                    case 0xb6:
                        (*p)++; // Next char is lwr
                        break;
                    default:
                        if (*p == 0xbf) {
                            *pExtChar = 0xcf;
                            (*p) = 0xb3;
                        }
                        break;
                    }
                case 0xce: // Greek & Coptic
                    if (*p == 0x86)
                        (*p) = 0xac;
                    else if (*p == 0x88)
                        (*p) = 0xad;
                    else if (*p == 0x89)
                        (*p) = 0xae;
                    else if (*p == 0x8a)
                        (*p) = 0xaf;
                    else if (*p == 0x8c) {
                        *pExtChar = 0xcf;
                        (*p) = 0x8c;
                    }
                    else if (*p == 0x8e) {
                        *pExtChar = 0xcf;
                        (*p) = 0x8d;
                    }
                    else if (*p == 0x8f) {
                        *pExtChar = 0xcf;
                        (*p) = 0x8e;
                    }
                    else if ((*p >= 0x91)
                        && (*p <= 0x9f))
                        (*p) += 0x20; // US ASCII shift
                    else if ((*p >= 0xa0)
                        && (*p <= 0xab)
                        && (*p != 0xa2)) {
                        *pExtChar = 0xcf;
                        (*p) -= 0x20;
                    }
                    break;
                case 0xcf: // Greek & Coptic
                    if (*p == 0x8f)
                        (*p) = 0xb4;
                    else if (*p == 0x91)
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0x98)
                        && (*p <= 0xaf)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    else if (*p == 0xb4)
                        (*p) = 0x91;
                    else if (*p == 0xb7)
                        (*p)++; // Next char is lwr
                    else if (*p == 0xb9)
                        (*p) = 0xb2;
                    else if (*p == 0xbb)
                        (*p)++; // Next char is lwr
                    else if (*p == 0xbd) {
                        *pExtChar = 0xcd;
                        (*p) = 0xbb;
                    }
                    else if (*p == 0xbe) {
                        *pExtChar = 0xcd;
                        (*p) = 0xbc;
                    }
                    else if (*p == 0xbf) {
                        *pExtChar = 0xcd;
                        (*p) = 0xbd;
                    }

                    break;
                case 0xd0: // Cyrillic
                    if ((*p >= 0x80)
                        && (*p <= 0x8f)) {
                        *pExtChar = 0xd1;
                        (*p) += 0x10;
                    }
                    else if ((*p >= 0x90)
                        && (*p <= 0x9f))
                        (*p) += 0x20; // US ASCII shift
                    else if ((*p >= 0xa0)
                        && (*p <= 0xaf)) {
                        *pExtChar = 0xd1;
                        (*p) -= 0x20;
                    }
                    break;
                case 0xd1: // Cyrillic supplement
                    if ((*p >= 0xa0)
                        && (*p <= 0xbf)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    break;
                case 0xd2: // Cyrillic supplement
                    if (*p == 0x80)
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0x8a)
                        && (*p <= 0xbf)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    break;
                case 0xd3: // Cyrillic supplement
                    if ((*p >= 0x81)
                        && (*p <= 0x8e)
                        && (*p % 2)) // Odd
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0x90)
                        && (*p <= 0xbf)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    break;
                case 0xd4: // Cyrillic supplement & Armenian
                    if ((*p >= 0x80)
                        && (*p <= 0xaf)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    else if ((*p >= 0xb1)
                        && (*p <= 0xbf)) {
                        *pExtChar = 0xd5;
                        (*p) -= 0x10;
                    }
                    break;
                case 0xd5: // Armenian
                    if ((*p >= 0x80)
                        && (*p <= 0x96)
                        && (!(*p % 2))) // Even
                        (*p)++; // Next char is lwr
                    break;
                case 0xe1: // Three byte code
                    pExtChar = p;
                    p++;
                    switch (*pExtChar) {
                    case 0x82: // Georgian
                        if ((*p >= 0xa0)
                            && (*p <= 0xbf)) {
                            *pExtChar = 0x83;
                            (*p) -= 0x10;
                        }
                        break;
                    case 0x83: // Georgian
                        if ((*p >= 0x80)
                            && ((*p <= 0x85)
                                || (*p == 0x87))
                            || (*p == 0x8d))
                            (*p) += 0x30;
                        break;
                    case 0xb8: // Latin extened
                        if ((*p >= 0x80)
                            && (*p <= 0xbf)
                            && (!(*p % 2))) // Even
                            (*p)++; // Next char is lwr
                        break;
                    case 0xb9: // Latin extened
                        if ((*p >= 0x80)
                            && (*p <= 0xbf)
                            && (!(*p % 2))) // Even
                            (*p)++; // Next char is lwr
                        break;
                    case 0xba: // Latin extened
                        if ((*p >= 0x80)
                            && (*p <= 0x94)
                            && (!(*p % 2))) // Even
                            (*p)++; // Next char is lwr
                        else if ((*p >= 0x9e)
                            && (*p <= 0xbf)
                            && (!(*p % 2))) // Even
                            (*p)++; // Next char is lwr
                        break;
                    case 0xbb: // Latin extened
                        if ((*p >= 0x80)
                            && (*p <= 0xbf)
                            && (!(*p % 2))) // Even
                            (*p)++; // Next char is lwr
                        break;
                    case 0xbc: // Greek extened
                        if ((*p >= 0x88)
                            && (*p <= 0x8f))
                            (*p) -= 0x08;
                        else if ((*p >= 0x98)
                            && (*p <= 0x9f))
                            (*p) -= 0x08;
                        else if ((*p >= 0xa8)
                            && (*p <= 0xaf))
                            (*p) -= 0x08;
                        else if ((*p >= 0xb8)
                            && (*p <= 0x8f))
                            (*p) -= 0x08;
                        break;
                    case 0xbd: // Greek extened
                        if ((*p >= 0x88)
                            && (*p <= 0x8d))
                            (*p) -= 0x08;
                        else if ((*p >= 0x98)
                            && (*p <= 0x9f))
                            (*p) -= 0x08;
                        else if ((*p >= 0xa8)
                            && (*p <= 0xaf))
                            (*p) -= 0x08;
                        else if ((*p >= 0xb8)
                            && (*p <= 0x8f))
                            (*p) -= 0x08;
                        break;
                    case 0xbe: // Greek extened
                        if ((*p >= 0x88)
                            && (*p <= 0x8f))
                            (*p) -= 0x08;
                        else if ((*p >= 0x98)
                            && (*p <= 0x9f))
                            (*p) -= 0x08;
                        else if ((*p >= 0xa8)
                            && (*p <= 0xaf))
                            (*p) -= 0x08;
                        else if ((*p >= 0xb8)
                            && (*p <= 0xb9))
                            (*p) -= 0x08;
                        break;
                    case 0xbf: // Greek extened
                        if ((*p >= 0x88)
                            && (*p <= 0x8c))
                            (*p) -= 0x08;
                        else if ((*p >= 0x98)
                            && (*p <= 0x9b))
                            (*p) -= 0x08;
                        else if ((*p >= 0xa8)
                            && (*p <= 0xac))
                            (*p) -= 0x08;
                        break;
                    default:
                        break;
                    }
                    break;
                case 0xf0: // Four byte code
                    pExtChar = p;
                    p++;
                    switch (*pExtChar) {
                    case 0x90:
                        pExtChar = p;
                        p++;
                        switch (*pExtChar) {
                        case 0x92: // Osage 
                            if ((*p >= 0xb0)
                                && (*p <= 0xbf)) {
                                *pExtChar = 0x93;
                                (*p) -= 0x18;
                            }
                            break;
                        case 0x93: // Osage 
                            if ((*p >= 0x80)
                                && (*p <= 0x93))
                                (*p) += 0x18;
                            break;
                        default:
                            break;
                        }
                        break;
                    default:
                        break;
                    }
                    break;
                default:
                    break;
                }
                pExtChar = 0;
            }
            p++;
        }
    }
    return pString;
}

int StrnCiCmp(const char *s1, const char *s2, size_t ztCount)
{
    if (s1 && *s1 && s2 && *s2) {
        char cExtChar = 0;
        unsigned char *pStr1Low = calloc(strlen(s1) + 1, sizeof(char));
        unsigned char *pStr2Low = calloc(strlen(s2) + 1, sizeof(char));
        if (pStr1Low && pStr2Low) {
            unsigned char *p1 = pStr1Low;
            unsigned char *p2 = pStr2Low;
            strcpy(pStr1Low, s1);
            strcpy(pStr2Low, s2);
            StrToLwrExt(pStr1Low);
            StrToLwrExt(pStr2Low);
            for (; ztCount--; p1++, p2++) {
                int iDiff = *p1 - *p2;
                if (iDiff != 0 || !*p1 || !*p2) {
                    free(pStr1Low);
                    free(pStr2Low);
                    return iDiff;
                }
            }
            free(pStr1Low);
            free(pStr2Low);
            return 0;
        }
        return (-1);
    }
    return (-1);
}
int StrCiCmp(const char *s1, const char *s2)
{
    return StrnCiCmp(s1, s2, (size_t)(-1));
}
char *StrCiStr(const char *s1, const char *s2)
{
    if (s1 && *s1 && s2 && *s2) {
        char *p = (char *)s1;
        size_t len = strlen(s2);
        while (*p) {
            if (StrnCiCmp(p, s2, len) == 0)
                return (char *)p;
            p++;
        }
    }
    return (0);
}
相关问题