Как удалить акценты и тильду в C++ std::string
-
02-07-2019 - |
Вопрос
У меня проблема со строкой на C++, в которой есть несколько слов на испанском языке.Это значит, что у меня много слов с ударениями и тильдами.Хочу заменить их на не акцентированные аналоги.Пример:Я хочу заменить это слово:«хабиа» вместо хабиа.Я попробовал заменить его напрямую, но с помощью метода replace класса строк, но мне не удалось заставить его работать.
Я использую этот код:
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="á-é-í-ó-ú-ñ-á-é-í-ó-ú-ñ";
Я явно что-то упускаю.Я не могу этого понять.Есть ли какая-нибудь библиотека, которую я могу использовать?
Спасибо,
Решение
Во-первых, это очень плохая идея:вы искажаете чей-то язык, удаляя буквы.Хотя дополнительные точки в таких словах, как «наивный», кажутся излишними людям, говорящим только по-английски, в мире существуют буквально тысячи систем письменности, в которых такие различия очень важны.Написание программного обеспечения, искажающего чью-то речь, ставит вас прямо на неправильную сторону противоречия между использованием компьютеров как средства расширения сферы человеческого самовыражения и использования компьютеров как средства расширения сферы человеческого самовыражения.инструменты угнетения.
По какой причине вы пытаетесь это сделать?Что-то дальше по линии задыхается от акцентов?Многие люди хотели бы помочь вам решить эту проблему.
Тем не менее, libicu может сделать это за вас.Открой трансформировать демо;скопируйте и вставьте текст на испанском языке в поле «Ввод»;входить
NFD; [:M:] remove; NFC
как «Соединение 1» и нажмите «Преобразовать».
(С помощью слайда 9 Преобразования Unicode в отделении интенсивной терапии.Слайды 29–30 показывают, как использовать API.)
Другие советы
Я не согласен с нынешним «одобренным» ответом.Этот вопрос имеет смысл, когда вы индексируете текст.Как и поиск без учета регистра, поиск без учета диакритических знаков является хорошей идеей."наивный" соответствует "Наивный" соответствует "наивный" соответствует "НАИВНЫЙ" (вы делать знаете, что заглавная буква «i» по-турецки означает «İ»?Вот почему вы игнорируете акценты)
Теперь лучший алгоритм намекается на утвержденный ответ:Используйте NKD (разложение), чтобы разложить буквы с ударением на основную букву и отдельный ударение, а затем удалить все ударения.
Однако в последующей перекомпозиции нет особого смысла.Вы удалили большинство последовательностей, которые могли измениться, а остальные в любом случае по сути идентичны.В чем разница между æ в NKC и æ в NKD?
Я определенно думаю, что вам следует разобраться в корне проблемы.То есть ищите решение, которое позволит вам поддерживать символы, закодированные в Юникоде или для локали пользователя.
При этом ваша проблема в том, что вы имеете дело с многосимвольными строками.Есть 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;
}
Возможно, вы захотите проверить повышение (http://www.boost.org/) библиотека.
У него есть библиотека регулярных выражений, которую вы можете использовать.Кроме того, у него есть специальная библиотека, в которой есть некоторые функции для манипулирования строками (связь), включая замену.
Попробуйте использовать std::wstring вместо std::string.UTF-16 должен работать (в отличие от ASCII).
Если вы можете (если вы используете 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;
Мне не удалось связать библиотеки 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 недель).
/// <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
}