Как убрать акценты и тильду в C++ std::string
У меня проблема со строкой в 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="á-é-í-ó-ú-ñ-á-é-í-ó-ú-ñ";
Я очевидно должен что-то упустить. Я не могу понять это. Есть ли библиотека, которую я могу использовать?
Спасибо,
8 ответов
Во-первых, это действительно плохая идея: вы искажаете чей-то язык, удаляя буквы. Хотя лишние точки в словах типа "наивный" кажутся излишними людям, которые говорят только по-английски, в мире существуют буквально тысячи систем письма, в которых такие различия очень важны. Написание программного обеспечения для искажения чьей-то речи прямо ставит вас на неверную сторону напряженности между использованием компьютеров в качестве средства расширения сферы человеческого выражения по сравнению с инструментами угнетения.
По какой причине вы пытаетесь это сделать? Что-то еще в дальнейшем задыхается от акцентов? Многие люди хотели бы помочь вам решить это.
Тем не менее, libicu может сделать это для вас. Откройте демо трансформации; скопируйте и вставьте текст на испанском языке в поле "Вход"; войти
NFD; [:M:] remove; NFC
как "Соединение 1" и нажмите трансформировать.
(С помощью слайда 9 " Преобразования Unicode в ICU". Слайды 29-30 показывают, как использовать API.)
Я не согласен с "одобренным" в настоящее время ответом. Этот вопрос имеет смысл, когда вы индексируете текст. Подобно поиску без учета регистра, поиск без учета акцента является хорошей идеей. "Наивный" соответствует "Наивный" соответствует "Наивный" соответствует "NAİVE" (вы знаете, что прописными буквами i на турецком языке? Поэтому вы игнорируете акценты)
Теперь лучший алгоритм намекает на утвержденный ответ: используйте NKD (разложение), чтобы разложить буквы с акцентом на основную букву и отдельный акцент, а затем уберите все акценты.
Впрочем, в перегруппировке нет особого смысла. Вы удалили большинство последовательностей, которые могли бы измениться, а остальные в любом случае идентичны. В чем разница между æ в NKC и æ в NKD?
Я определенно думаю, что вы должны изучить корень проблемы. То есть ищите решение, которое позволит вам поддерживать символы, закодированные в Unicode или для локали пользователя.
При этом ваша проблема в том, что вы имеете дело с многосимвольными строками. Есть 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;
}
Я удивлен, что некоторые люди говорят, что нельзя деактивировать персонажей. Акцент на символах в именах файлов может привести к множеству проблем при использовании программ, явно написанных программистами, которые этого не допускали.
Я полностью на 100% за использование Unicode и не теряю важную информацию, такую как акценты, но иногда вам нужно сделать что-то подобное. Лучше не переоценивать причины, по которым люди хотят получить определенную функцию. В моем случае я собираюсь сделать это для поиска "похожих" текстов (что часто означает, что тексты написаны - неправильно - без акцентов).
У кого-то всегда будет веская причина.
Я использовал Unix, я забыл упомянуть об этом, но я запускаю tr, как это
$tr ééíóú aeiou
-е-е-ó-ú
уй-ио-уу-уу-уу
это не работает, как предполагалось. Я думаю, что это связано с Unicode и строковым классом.
Дело в том, что я разрабатываю заявку, которая должна поступить в университет в течение 5 дней. Это программа, которая будет индексировать текст внутри тега на страницах HTML (я не могу использовать apache lucene для создания индекса). Однако я не буду индексировать все слова, должен удалить все стоп-слова, используя основание, и сделать весь текст строчными. По просьбе нашего учителя мы должны устранить акценты и тильду в словах. Надеюсь, это немного прояснит ситуацию.
Saludos,
Вы можете проверить библиотеку надстройки ( http://www.boost.org/).
Он имеет библиотеку регулярных выражений, которую вы можете использовать. Кроме того, он имеет специальную библиотеку, которая имеет некоторые функции для работы со строками ( ссылка), включая замену.
Я не мог связать библиотеки 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 недель).
Попробуйте использовать std::wstring вместо std::string. UTF-16 должен работать (в отличие от ASCII).
/// <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
}
Если вы можете (если вы используете 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;