Как удалить акценты и тильду в С++ std::string

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

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

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

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

Спасибо,

Ответ 1

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

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

Тем не менее, libicu может сделать это за вас. Откройте трансформировать демо; скопируйте и вставьте свой испанский текст в поле "Вход"; введите

NFD; [:M:] remove; NFC

как "Соединение 1" и щелкните преобразование.

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

Ответ 2

Я не согласен с утвержденным в настоящее время ответом. Вопрос имеет смысл, когда вы индексируете текст. Подобно нечувствительным к регистру поискам, поиск без учета акцентов - хорошая идея. "наивные" матчи "Наивные" соответствуют "наивным" матчам "NAİVE" (вы знаете, что в верхнем регистре я İ на турецком? Почему вы игнорируете акценты)

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

Тем не менее, в перекомпозиции мало смысла. Вы удалили большинство последовательностей, которые изменились бы, а остальные все равно были бы идентичны. Какая разница между æ в НКК и æ в НКД?

Ответ 3

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

Говоря, ваша проблема заключается в том, что вы имеете дело с многосимвольными строками. Существует 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;
}

Ответ 4

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

Ответ 5

Я полностью 100% в пользу использования Unicode и не теряю важной информации, такой как акценты, но иногда вам нужно сделать что-то вроде этого. Лучше не предполагать, что люди предпочитают какую-то конкретную функцию. В моем случае я хочу сделать это для поиска "похожих" текстов (что часто означает, что тексты написаны неправильно - без акцентов).

У кого-то всегда будет веская причина.

Ответ 6

Если вы можете (если вы используете Unix), я предлагаю использовать tr для этого: он выполнен на заказ для этой цели. Помните, что нет кода == no buggy code.: -)

Изменить: Извините, вы правы, tr, похоже, не работает. Как насчет sed? Это довольно глупый script, который я написал, но он работает для меня.

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

Ответ 7

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

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

Ответ 8

Я использовал unix, я забыл упомянуть об этом, но я запускаю tr как это

$tr áéíóú aeiou
-е-е-ó-ú
уй-ио-уу-уу-уу

он не работает, как ожидалось. Я думаю, что это связано с unicode и строковым классом.

Ответ 9

Дело в том, что я разрабатываю заявку в течение 5 дней для университета. Это программа, которая будет индексировать текст внутри тега в HTML-страницах (я не могу использовать apache lucene для создания индекса). Однако я не буду индексировать все слова, должен удалить все стоп-слова, используя их, и сделать весь текст в нижнем регистре. По просьбе нашего учителя мы должны исключить акценты и тильду в словах. Надеюсь, что это немного упростит.

Saludos,

Ответ 10

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

Ответ 11

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