具有非 ASCII 字符的反向字符串

Reverse string with non-ASCII characters

本文关键字:字符串 字符 ASCII      更新时间:2023-10-16

我想用这样的特殊字符更改字符串中的顺序:

ZAŻÓŁĆ GĘŚLĄ JAŹŃ

ŃŹAJ ĄŁŚĘG ĆŁÓŻAZ

我尝试使用标准::反向

std::string text("ZAŻÓŁĆ GĘŚLĄ JAŹŃ!");
std::cout << text << std::endl;
std::reverse(text.rbegin(), text.rend());
std::cout << text << std::endl;

但输出显示:

ZAŻÓŁĆ GĘŚLĄ JAŹŃ!

!\203Ź\305AJ \204\304L\232Ř\304G \206āœû\305AZ <- 反转字符串

所以我尝试"手动"执行此操作:

std::string text1("ZAŻÓŁĆ GĘŚLĄ JAŹŃ!");
std::cout << text1 << std::endl;
int count = (int) floorf(text1.size() /2.f);
std::cout << count  << "  " << text1.size() << std::endl;
unsigned int maxIndex = text1.size() - 1;
for (int i = 0; i < count ; i++)
{
    char tmp = text1[i];
    text1[i] = text1[maxIndex];
    text1[maxIndex] = tmp;
    maxIndex--;
}
std::cout << text1 << std::endl;

但是在这种情况下,我在text1.size()中遇到了问题,因为每个特殊字符都被计算两次:

ZAŻÓŁĆ GĘŚLĄ JAŹŃ!

13 27 <- 第二个数字是 text1.size()

!\203Ź\305AJ \204\304L\232Ř\304G \206āœû\305AZ

如何正确反转带有特殊字符的字符串?

您的代码确实可以正确反转字符串中的字节,这里没有错。但是,问题在于编译器将文字字符串"ZAŻÓŁĆ GĘŚLĄ JAŹŃ!"存储在UTF-8编码中。

UTF-8 将除与 ASCII 匹配的字符之外的所有字符存储为可变长度的字节序列。这意味着一个char(一个字节)不再是一个字符,因此反转char现在与反转字符不同。

要实现您的目标,您至少有两种选择:

  1. 使用一些 utf-8 库,它可以让你迭代字符而不是字节。一个例子是 http://utfcpp.sourceforge.net/
  2. 不知何故(这在很大程度上取决于您使用的编译器和操作系统)切换到具有恒定字符长度并具有良好旧的恒定字符大小字符串的 utf-32 编码,而没有所有这些疯狂的可变字符大小问题。

UPD:一个很好的链接:http://www.joelonsoftware.com/articles/Unicode.html

你可以自己编写一个 reverseUt8 函数:

std::string getMultiByteReversed(char ch1, char ch2)
{  
   if (ch == 'xc3') // most utf8 characters
      return std::string(ch1)+ std::string(ch2);
   } else {
      return std::string(ch1);
   }
}
std::string reverseMultiByteString(const std::string &s)
{
    std::string result;
    for (std::string::reverse_iterator it = s.rbegin(); it != s.rend(); ++it) {
       std::string reversed;
       if ( (it+1) != rbegin() && (reversed = getMultiByteReversed(*it, *it+1) ) {
          result += reversed;
          ++it;
       } else {
          result += *it;
       }
  }
  return result;
}

您可以在以下位置查找utf8代码: http://www.utf8-chartable.de/

这里有几个问题。答案很复杂,可能取决于您要做什么。

首先(正如其他答案所述)如果您的字符串是 UTF-8 编码的,则一个 Unicode 代码点可能包含多个字节。如果只是反转字节,则会破坏 UTF-8 编码。对此最简单(尽管不一定是最好的)解决方法是将字符串转换为 UTF-32 并反转 32 位代码点而不是字节。

下一个问题是单个字素可能包含多个 Unicode 代码点。例如,"é"可能编码为两个代码点 U+0065,后跟 U+0301。如果颠倒了这些字符的顺序,这将破坏它,因为组合字符 U+301 现在将与不同的基本字符相关联。因此,以这种方式颠倒的"神奇宝贝"将变成"noḿekoP",重音在"m"而不是"e"上。

现在您可能认为可以通过先将字符串规范化为组合形式来解决此问题。然而,这有其自身的问题,因为并非每个字素都可以用单个代码点表示。例如,加拿大国旗表情符号 ( ) 由代码点 U+1F1E8 后跟代码点 U+1F1E6 表示。它没有单一的代码点。如果反转其代码点,则会获得阿森松岛 ( ) 的标志。

然后你有一些语言,其中字符根据上下文改变形式,我还不太知道如何处理这些。

它可能更接近您想要反转字素簇的内容。请参阅 UAX29:Unicode 文本分割。

您是否尝试过逐个交换字符。例如,如果字符串长度为奇数,请将第一个字符与最后一个字符交换,将第二个字符与倒数第二个字符交换,直到剩下中间字符。如果字符串长度为偶数,请将第一个与最后一个交换,将第二个与倒数第二个交换,直到交换两个中间字符。这样,字符串将被反转。