Как убрать акценты и тильду в C ++ std :: string - PullRequest
14 голосов
/ 28 сентября 2008

У меня проблема со строкой в ​​C ++, в которой есть несколько слов на испанском языке. Это значит, что у меня много слов с акцентами и тильдами. Я хочу заменить их на их не акцентированные аналоги. Пример: я хочу заменить это слово: "había" на хабия. Я попытался заменить его напрямую, но с помощью метода замены строкового класса, но я не смог заставить его работать.

Я использую этот код:

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

Я, очевидно, что-то упускаю. Я не могу понять это. Могу ли я использовать какую-нибудь библиотеку?

Спасибо

Ответы [ 8 ]

23 голосов
/ 29 сентября 2008

Я не согласен с «одобренным» в настоящее время ответом. Этот вопрос имеет смысл, когда вы индексируете текст. Подобно поиску без учета регистра, поиск без учета акцента - хорошая идея. "Наивный" соответствует "Наивный" соответствует "Наивный" соответствует "NAİVE" (вы делаете знаете, что прописными буквами i на турецком языке? Поэтому вы игнорируете акценты)

Теперь лучший алгоритм намекает на утвержденный ответ: используйте NKD (разложение), чтобы разложить акцентированные буквы на основную букву и отдельный акцент, а затем уберите все акценты.

Впрочем, после перекомпоновки мало что значит. Вы удалили большинство последовательностей, которые могли бы измениться, а остальные в любом случае идентичны. В чем разница между æ в NKC и æ в NKD?

17 голосов
/ 28 сентября 2008

Во-первых, это действительно плохая идея: вы искажаете чей-то язык, удаляя буквы. Хотя лишние точки в словах типа «наивный» кажутся излишними людям, которые говорят только по-английски, в мире существуют буквально тысячи систем письма, в которых такие различия очень важны. Написание программного обеспечения для искажения чьей-то речи прямо ставит вас на неверную сторону напряженности между использованием компьютеров в качестве средства расширения сферы человеческого выражения и инструментов угнетения.

По какой причине вы пытаетесь это сделать? Что-то еще в дальнейшем задыхается от акцентов? Многие люди хотели бы помочь вам решить эту проблему.

Тем не менее, libicu может сделать это для вас. Откройте демонстрационную программу transform ; скопируйте и вставьте текст на испанском языке в поле «Вход»; введите

NFD; [:M:] remove; NFC

как «Соединение 1» и нажмите «transform».

(С помощью слайда 9 Преобразования Unicode в ICU . Слайды 29-30 показывают, как использовать API.)

2 голосов
/ 28 сентября 2008

Я определенно думаю, что вы должны изучить корень проблемы. То есть ищите решение, которое позволит вам поддерживать символы, закодированные в Юникоде или для локали пользователя.

При этом ваша проблема в том, что вы имеете дело с многосимвольными строками. Есть std::wstring, но я не уверен, что использовал бы это. Во-первых, широкие символы не предназначены для обработки кодировок переменной ширины. Эта дыра углубляется, поэтому я оставлю ее на этом.

Теперь, что касается остальной части вашего кода, он подвержен ошибкам, потому что вы смешиваете логику циклов с логикой перевода. Таким образом, могут возникнуть как минимум два вида ошибок: ошибки перевода и ошибки зацикливания. Используйте STL, это может вам сильно помочь с циклической частью.

Ниже приводится грубое решение для замены символов в строке.

main.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;
}
0 голосов
/ 24 октября 2018
    /// <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
    }
0 голосов
/ 29 сентября 2008

Мне не удалось связать библиотеки 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 недель).

0 голосов
/ 28 сентября 2008

Попробуйте использовать std :: wstring вместо std :: string. UTF-16 должен работать (в отличие от ASCII).

0 голосов
/ 28 сентября 2008

Возможно, вы захотите проверить повышение (http://www.boost.org/) библиотека.

У него есть библиотека регулярных выражений, которую вы можете использовать. Кроме того, он имеет специальную библиотеку, в которой есть некоторые функции для работы со строками ( ссылка ), включая замену.

0 голосов
/ 28 сентября 2008

Если вы можете (если вы используете 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;
...