17

我对 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="á-é-í-ó-ú-ñ-á-é-í-ó-ú-ñ";

我显然必须遗漏一些东西。我想不通。有没有我可以使用的图书馆?

谢谢,

4

8 回答 8

30

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

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

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

于 2008-09-29T12:43:41.133 回答
20

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

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

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

NFD; [:M:] remove; NFC

作为“Compound 1”并单击变换。

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

于 2008-09-28T00:02:25.137 回答
2

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

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

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

以下是替换字符串中字符的粗略解决方案。

主.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;
}

翻译字符.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

翻译字符.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;
}
于 2008-09-28T06:47:56.537 回答
0

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

它有一个正则表达式库,您可以使用它。此外,它还有一个特定的库,该库具有一些用于字符串操作(链接)的功能,包括替换。

于 2008-09-27T23:55:07.870 回答
0

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

于 2008-09-28T00:26:33.660 回答
0

我无法链接 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 周后),我会更改它。

于 2008-09-29T03:05:29.680 回答
-1

如果可以(如果您正在运行 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;
于 2008-09-27T23:43:58.380 回答
-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
    }
于 2018-10-24T14:52:53.947 回答