【问题标题】:How to remove accents and tilde in a C++ std::string如何删除 C++ std::string 中的重音符号和波浪号
【发布时间】:2010-09-13 18:31:29
【问题描述】:

我对 C++ 中的一个字符串有疑问,该字符串在西班牙语中有几个单词。这意味着我有很多带有重音符号和波浪线的单词。我想将它们替换为没有重音的对应物。示例:我想替换这个词:“había”代表 habia。我尝试直接替换它,但使用字符串类的替换方法,但我无法让它工作。

我正在使用此代码:

for (it= dictionary.begin(); it != dictionary.end(); it++)
{
    strMine=(it->first);
    found=toReplace.find_first_of(strMine);
    while (found!=std::string::npos)
    {
        strAux=(it->second);
        toReplace.erase(found,strMine.length());
        toReplace.insert(found,strAux);
        found=toReplace.find_first_of(strMine,found+1);
    }
}

dictionary 是这样的地图(有更多条目):

dictionary.insert ( std::pair<std::string,std::string>("á","a") );
dictionary.insert ( std::pair<std::string,std::string>("é","e") );
dictionary.insert ( std::pair<std::string,std::string>("í","i") );
dictionary.insert ( std::pair<std::string,std::string>("ó","o") );
dictionary.insert ( std::pair<std::string,std::string>("ú","u") );
dictionary.insert ( std::pair<std::string,std::string>("ñ","n") );

toReplace 字符串是:

std::string toReplace="á-é-í-ó-ú-ñ-á-é-í-ó-ú-ñ";

我显然一定错过了什么。我想不通。 有没有我可以使用的库?

谢谢,

【问题讨论】:

  • 您应该添加您所针对的平台(Windows、Linux 等),以及您所针对的编码(UTF-8、UTF-16 等)。例如,您的“á”是字形 E1,它在 USO-8859-1 char 上翻译 'á',在 UTF-16 wchar_t 上翻译 L'á',但在 UTF-8 上翻译“á”(是的,两个字符)
  • 对不起...当我回到您的帖子(通过在 Unicode.org 上搜索)并验证评论时,您确实回答了...
  • 这是 stackoverflow.com/questions/140422/…"> 如何将 8 位字符转换为 7 位字符的副本? (即 Ü 到 U).

标签: c++ string text str-replace


【解决方案1】:
    /// <summary>
    /// 
    /// Replace any accent and foreign character by their ASCII equivalent.
    /// In other words, convert a string to an ASCII-complient string.
    /// 
    /// This also get rid of special hidden character, like EOF, NUL, TAB and other '\0', except \n\r
    /// 
    /// Tests with accents and foreign characters:
    /// Before: "äæǽaeöœoeüueÄAeÜUeÖOeÀÁÂÃÄÅǺĀĂĄǍΑΆẢẠẦẪẨẬẰẮẴẲẶАAàáâãåǻāăąǎªαάảạầấẫẩậằắẵẳặаaБBбbÇĆĈĊČCçćĉċčcДDдdÐĎĐΔDjðďđδdjÈÉÊËĒĔĖĘĚΕΈẼẺẸỀẾỄỂỆЕЭEèéêëēĕėęěέεẽẻẹềếễểệеэeФFфfĜĞĠĢΓГҐGĝğġģγгґgĤĦHĥħhÌÍÎÏĨĪĬǏĮİΗΉΊΙΪỈỊИЫIìíîïĩīĭǐįıηήίιϊỉịиыїiĴJĵjĶΚКKķκкkĹĻĽĿŁΛЛLĺļľŀłλлlМMмmÑŃŅŇΝНNñńņňʼnνнnÒÓÔÕŌŎǑŐƠØǾΟΌΩΏỎỌỒỐỖỔỘỜỚỠỞỢОOòóôõōŏǒőơøǿºοόωώỏọồốỗổộờớỡởợоoПPпpŔŖŘΡРRŕŗřρрrŚŜŞȘŠΣСSśŝşșšſσςсsȚŢŤŦτТTțţťŧтtÙÚÛŨŪŬŮŰŲƯǓǕǗǙǛŨỦỤỪỨỮỬỰУUùúûũūŭůűųưǔǖǘǚǜυύϋủụừứữửựуuÝŸŶΥΎΫỲỸỶỴЙYýÿŷỳỹỷỵйyВVвvŴWŵwŹŻŽΖЗZźżžζзzÆǼAEßssIJIJijijŒOEƒf'ξksπpβvμmψpsЁYoёyoЄYeєyeЇYiЖZhжzhХKhхkhЦTsцtsЧChчchШShшshЩShchщshchЪъЬьЮYuюyuЯYaяya"
    /// After:  "aaeooeuueAAeUUeOOeAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaBbCCCCCCccccccDdDDjddjEEEEEEEEEEEEEEEEEEeeeeeeeeeeeeeeeeeeFfGGGGGgggggHHhhIIIIIIIIIIIIIiiiiiiiiiiiiJJjjKKkkLLLLllllMmNNNNNnnnnnOOOOOOOOOOOOOOOOOOOOOOooooooooooooooooooooooPpRRRRrrrrSSSSSSssssssTTTTttttUUUUUUUUUUUUUUUUUUUUUUUUuuuuuuuuuuuuuuuuuuuuuuuYYYYYYYYyyyyyyyyVvWWwwZZZZzzzzAEssIJijOEf'kspvmpsYoyoYeyeYiZhzhKhkhTstsChchShshShchshchYuyuYaya"
    /// 
    /// Tests with invalid 'special hidden characters':
    /// Before: "\0\0\000\0000Bj��rk�\'\"\\\0\a\b\f\n\r\t\v\u0020���oacu\'\\\'te�"
    /// After:  "00000Bjrk'\"\\\n\r oacu'\\'te"
    /// 
    /// </summary>
    private string Normalize(string StringToClean)
    {
        string normalizedString = StringToClean.Normalize(NormalizationForm.FormD);
        StringBuilder Buffer = new StringBuilder(StringToClean.Length);

        for (int i = 0; i < normalizedString.Length; i++)
        {
            if (CharUnicodeInfo.GetUnicodeCategory(normalizedString[i]) != UnicodeCategory.NonSpacingMark)
            {
                Buffer.Append(normalizedString[i]);
            }
        }

        string PreAsciiCompliant = Buffer.ToString().Normalize(NormalizationForm.FormC);
        StringBuilder AsciiComplient = new StringBuilder(PreAsciiCompliant.Length);

        foreach (char character in PreAsciiCompliant)
        {
            //Reject all special characters except \n\r (Carriage-Return and Line-Feed). 
            //Get rid of special hidden character, like EOF, NUL, TAB and other '\0'
            if (((int)character >= 32 && (int)character < 127) || ((int)character == 10 || (int)character == 13)) 
            {
                AsciiComplient.Append(character);
            }
        }
        return AsciiComplient.ToString().Trim(); // Remove spaces at start and end of string if any
    }

【讨论】:

  • 如果我没记错的话,这是来自 Java,为什么要包含它,如果它说“C”?
【解决方案2】:

我不同意当前“已批准”的答案。当您为文本编制索引时,这个问题非常有意义。与不区分大小写的搜索一样,不区分重音的搜索也是一个好主意。 "naïve" 匹配 "Naïve" 匹配 "naive" 匹配 "NAİVE"(你确实知道土耳其语中大写的 i 是 İ?这就是你忽略重音符号的原因)

现在,最佳算法暗示了已获批准的答案:使用 NKD(分解)将重音字母分解为基本字母和单独的重音,然后删除所有重音。

不过,之后的重新构图没什么意义。您删除了大多数会更改的序列,而其他序列的所有意图和目的无论如何都是相同的。 NKC中的æ和NKD中的æ有什么区别?

【讨论】:

  • 你的理论在德语中崩溃了。 "bär" (bear) 将与 "baer" (bear) 比较,但不会与 "bar" (bar) 比较。
  • 其实不然。 bär 的 Unicode 分解给出了 ba"r(对变音符号使用额外的代码点),而不是 baer。请记住 Unicode 分解与语言环境无关。ä = ae 是德语,但不是荷兰语分解。
  • 我认为您的意思是“İ”,而不是“Ï”代表大写字母“i”。
  • 也许你可以指出这些 NKC / NKD 东西的一些定义?
【解决方案3】:

我无法链接 ICU 库,但我仍然认为这是最好的解决方案。因为我需要这个程序尽快运行,所以我做了一个小程序(我必须改进),我将使用它。谢谢大家的建议和回答。

这是我要使用的代码:

for (it= dictionary.begin(); it != dictionary.end(); it++)
{
    strMine=(it->first);
    found=toReplace.find(strMine);
    while (found != std::string::npos)
    {
        strAux=(it->second);
        toReplace.erase(found,2);
        toReplace.insert(found,strAux);
        found=toReplace.find(strMine,found+1);
    }
} 

下次我必须提交我的程序进行更正时(大约 6 周后),我会更改它。

【讨论】:

    【解决方案4】:

    我绝对认为您应该调查问题的根源。也就是说,寻找一种解决方案,让您支持以 Unicode 编码的字符或用户的区域设置。

    话虽如此,您的问题是您正在处理多字符串。有std::wstring,但我不确定我会使用它。一方面,宽字符并不意味着处理可变宽度编码。这个洞很深,所以我就留着吧。

    现在,至于您的其余代码,它很容易出错,因为您将循环逻辑与翻译逻辑混合在一起。因此,至少会出现两种错误:翻译错误和循环错误。请务必使用 STL,它可以在循环部分为您提供很多帮助。

    下面是替换字符串中字符的粗略方案。

    ma​​in.cpp

    #include <iostream>
    #include <string>
    #include <iterator>
    #include <algorithm>
    #include "translate_characters.h"
    
    using namespace std;
    
    int main()
    {
        string text;
        cin.unsetf(ios::skipws);
        transform(istream_iterator<char>(cin), istream_iterator<char>(),
                  inserter(text, text.end()), translate_characters());
        cout << text << endl;
        return 0;
    }
    

    translate_characters.h

    #ifndef TRANSLATE_CHARACTERS_H
    #define TRANSLATE_CHARACTERS_H
    
    #include <functional>
    #include <map>
    
    class translate_characters : public std::unary_function<const char,char> {
    public:
        translate_characters();
        char operator()(const char c);
    
    private:
        std::map<char, char> characters_map;
    };
    
    #endif // TRANSLATE_CHARACTERS_H
    

    translate_characters.cpp

    #include "translate_characters.h"
    
    using namespace std;
    
    translate_characters::translate_characters()
    {
        characters_map.insert(make_pair('e', 'a'));
    }
    
    char translate_characters::operator()(const char c)
    {
        map<char, char>::const_iterator translation_pos(characters_map.find(c));
        if( translation_pos == characters_map.end() )
            return c;
        return translation_pos->second;
    }
    

    【讨论】:

    • 您正在映射 。但是 utf-8 "ñ" (例如)不是(相当于)一个字符(实际上是一个 2 字节的东西)。这是一种很好的即时技术,但它比我想象的要复杂得多。
    【解决方案5】:

    如果可以(如果您正在运行 Unix),我建议为此使用tr 工具:它是为此目的而定制的。请记住,没有代码 == 没有错误代码。 :-)

    编辑:对不起,你是对的,tr 似乎不起作用。 sed 怎么样?这是我写的一个非常愚蠢的脚本,但它对我有用。

    #!/bin/sed -f
    s/á/a/g;
    s/é/e/g;
    s/í/i/g;
    s/ó/o/g;
    s/ú/u/g;
    s/ñ/n/g;
    

    【讨论】:

      【解决方案6】:

      尝试使用 std::wstring 代替 std::string。 UTF-16 应该可以工作(而不是 ASCII)。

      【讨论】:

        【解决方案7】:

        首先,这是一个非常糟糕的主意:您通过删除字母来破坏某人的语言。尽管像“naïve”这样的单词中多余的点对于只会说英语的人来说似乎是多余的,但世界上确实有成千上万的书写系统,其中这种区别非常重要。编写软件来破坏某人的言论会让你在使用计算机作为扩大人类表达领域的手段与压迫工具之间的紧张关系中处于错误的一边。

        您尝试这样做的原因是什么?是不是更深层次的东西让口音窒息了?很多人都愿意帮助您解决这个问题。

        也就是说,libicu 可以为您做到这一点。打开transform demo;将您的西班牙语文本复制并粘贴到“输入”框中;进入

        NFD; [:M:] remove; NFC
        

        作为“复合 1”并单击变换。

        (在Unicode Transforms in ICU 的幻灯片 9 的帮助下。幻灯片 29-30 展示了如何使用 API。)

        【讨论】:

        • 嗯,我来自阿根廷,这是一个讲西班牙语的国家,所以第一部分我已经很熟悉了。让我在下面的答案中提供更多细节。
        • 正确!口音和波浪线不是为了可爱;砍掉它们会改变文本的含义。 “Habia”不是一个词,但“había”是。 “性格”是“个性”; “字符”是印刷符号。 “迦拿”是白发; “Caña”是一根手杖。 “比索”是名词。 “比索”是动词。
        • 顺便说一句。我发现这个页面解释了如何使用 ICU Transliterator:markcmusic.com/blog/2008/08/28/using-the-icu4c-transliterator
        • 虽然这在理论上是正确的,但在实践中,许多讲西班牙语的人不会费心使用口音,或者只是把它们弄错(我想到了 IM),而且意思仍然很清楚。这就像它/它的,他们是/他们的,等等用英语。错误地使用它们会显示出一些疏忽,但很少会引起误解。
        • 在荷兰语中,重音用于强调。 “een”是荷兰语中“an”或“one”的意思,如“een appel”。但如果你想精确地强调一个,你可以添加重音:“één appel”
        【解决方案8】:

        您可能想查看 boost (http://www.boost.org/) 库。

        它有一个正则表达式库,您可以使用它。 此外,它还有一个特定的库,其中包含一些用于字符串操作 (link) 的函数,包括替换。

        【讨论】:

          猜你喜欢
          • 2012-06-22
          • 1970-01-01
          • 2015-08-08
          • 1970-01-01
          • 1970-01-01
          • 2011-11-04
          • 2012-10-20
          • 2012-12-04
          相关资源
          最近更新 更多