【问题标题】:Getting Segmentation Fault error while replacing unicode to a whitespace in string of C++将 unicode 替换为 C++ 字符串中的空格时出现分段错误错误
【发布时间】:2018-12-13 23:37:05
【问题描述】:

我正在尝试将 Unicode Character 'NO-BREAK SPACE' (U+00C2)Â 替换为字符串 str 中的空格“”,而在某些情况下运行它给了我分段错误。谁能建议我这是无效的内存访问。
我做得对吗?
还有其他方法吗?

string str = "transaction applies: Â Â Â Â Â Â Â Â Â {79}";
void cleanup(string& str)
    {   
        string unicode = "\u00C2";
        size_t pos = str.find(unicode);
        while(str.find(unicode, pos)!=string::npos && pos != str.length())
        {   
            pos = str.find(unicode, pos);
            str.replace(pos, unicode.length(), " " ); //unicode replace by a space  
            // this above line is giving segmentation fault
            pos = pos + unicode.length();
        }
        return;
    }

输出:

【问题讨论】:

  • 你想多了。 for (char &c:str) {if (c == (char)0xA0) c=' ';}
  • 将 NBSP 等同于 Â 令人困惑。我建议您更准确地了解您的字符串实际上是什么。这两者可能在不同的编码中具有相同的字节序列,但它们是非常不同的东西。这有点像取一个整数,将位重新解释为双精度值,然后将得到的双精度值与整数值相等。
  • @SamVarshavchik 感谢您的输入,但它不会更改字符串 str,即不会将 Â 替换为 ' '(空格)。
  • Â 是 U+00C2 而 nbsp 是 U+00A0。这是两个不同的代码点。
  • @SamVarshavchik Unicode 代码点 A0 在 UTF-8 中编码为 C2 A0。仅仅替换 A0 字节是不够的。这里的问题可以通过一个调试器或者一堆printfs来追踪来解决。

标签: c++ string unicode replace


【解决方案1】:

只要这样做就足够了。

std::string str("transaction applies: Â Â Â Â Â Â Â Â Â {79}");
std::transform(str.begin(), str.end(), str.begin(), [](char c) -> char {return (c == (char)'\u00C2') ? ' ': c ; });

【讨论】:

  • 我收到此错误error: multi-character character constant [-Werror=multichar] std::transform(str.begin(), str.end(), str.begin(), [](char c) -> char {return (c == (char)'\u00C2') ? ' ': c ; });
  • 要替换单个char 值,可以使用std:replace() 代替std::transform(),例如:std::replace(str.begin(), str.end(), '\xC2', ' ');
【解决方案2】:

unicode 字符与其 utf-8 表示之间存在混淆。 NO-BREAK SPACE 确实是 unicode 字符 U+00A0,它的 utf-8 表示是 "\xc2\xa0"。而Â 是带有 CIRCUMFLEX 的拉丁大写字母 A,或 unicode 字符 U+00C2,其 unicode 表示为 "\xc3\x82"

这意味着您的初始字符串不包含任何 NO-BREAK SPACE。如果您的编辑器字符集是 Latin1 或 windows cp1252,它将包含重复的 "\xc2\x20"(即 latin1 编码的 'Â' 和空格),如果它是 utf8,它将包含重复的 "\xc3\x82\x20 "(即 utf8 编码的 'Â' 和空格)。然后,当您搜索“\u00A0”的出现时,实际上是在搜索原始字符串中不存在的“\xc2\xa0”的出现。分段错误是由posstd::string::npos 引起的:str.find(unicode, pos) 调用未定义的行为。

做什么:选择你的阵营。当您使用窄字符串时,您必须决定使用什么编码。如果您使用 utf8(在 Linux 世界中很常见),那么 NO-BREAK SPACE 字符是一个长度为 2 个字符的字符串:{ 0xc2, 0xa0 }。而这一行:

string unicode = "\u00A0";

和这个一模一样:

string unicode = "\xc2\xa0";

最重要的是,在使用它之前,您必须拥有一个有效的 pos:

   ...
   size_t pos = str.find(unicode);
   if (pos == string::npos) return;
   ...

【讨论】:

    【解决方案3】:

    主要问题是您将所有字符替换为空格,然后搜索下一个特殊字符。在您尝试替换不存在的字符后 -- pos = string::npos

    这里是您的 while 循环的简单修改,以便它工作(有用的 cout 包括跟踪):

    void cleanup(string& str) {
      string unicode = "\u00C2"; // \u00C2 is Â
      size_t pos = str.find(unicode);
      while(pos != string::npos) {
        cout << "str: " << str << "\tpos: " << pos << endl;
        str.replace(pos, unicode.length(), " ");
        pos = pos + unicode.length();
        pos = str.find(unicode, pos);
      }
    }
    

    您可以(并且可能应该)将其修改为针对字符串字符的 for-each 循环,或用于简化代码的 do-while 循环。但它不起作用的原因如上所述,您尝试替换不存在的pos

    并不是说这是最好的程序,而是可能转换为 for-each 循环,所以你可以在 C++11 中看到一个例子:

    #include <locale>
    #include <codecvt>
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    // Using u16string because of unicode characters
    void cleanup(u16string& str) {
      for(auto& c : str)
        if(c == u'\u00C2')
          c = u' ';
    }
    
    int main() {
      u16string str = u"transaction applies: \u00C2 \u00C2 \u00C2 \u00C2 \u00C2 \u00C2 \u00C2 \u00C2 \u00C2 {79}";
      cleanup(str);
    
      wstring_convert<codecvt_utf8<char16_t>, char16_t> convert;
      cout << convert.to_bytes(str) << endl;
    }
    

    【讨论】:

    • 这可能是字符串的编码。请参阅我的 u16string 示例以查看可能的编码。基本上,在您的编辑器中不要使用文字 Â。在字符串文字中使用 \u00C2。
    猜你喜欢
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多