LINUX.ORG.RU

Удалить из строки ударение и подобное

 , , , ,


1

3

Всем здрасьте.
Взял я boost::locale для операций с Юникодом, и пока не понял - а как удалить из строки знаки ударения (хотя там может быть и другой мусор однозначно). С наскоку сделал декомпозицию с case folding’ом - не помогает:

#include <boost/locale.hpp>
#include <iostream>
#include <string>
#include <locale>
using namespace boost::locale;
using namespace std;
 
int main() {
	wstring str = L"фыва\u00b4"; // "фыва" с ударением на 'а'
	generator gen;
	locale::global(gen(""));
	wstring res = normalize(str, boost::locale::norm_nfd);
	fold_case(res);
	cout << str.size() << "  " << res.size() << endl;
}

// cout: 5 5

На стековерфлоу есть подобный вопрос, там советовали вручную парсить строку для поиска ударений, это явно странно. Как правильно?

★★

Последнее исправление: kvpfs (всего исправлений: 3)

Похоже, что для этого надо юзать ICU напрямую. Хз, не разобрался. Есть collator, но только сравнение да одни лишь accents игнорить не умеет, как минимум ещё и регистр.

kvpfs ★★
() автор топика
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

вручную парсить строку для поиска ударений

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

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Так то да, есть пример. Какая-же ужасная документация у ICU, не хватает юзер гида с хелоуворлдами. Вместо этого какая-то шелуха маркетинговая воспевающая Юникод.

Ещё имеется вопрос по ICU - например, я хочу итерироваться по символам в строке, для этого делаю:

UErrorCode ec = U_ZERO_ERROR;
icu::Locale loc = icu::Locale("ru");
icu::BreakIterator::createCharacterInstance(loc, ec);

И вот здесь у меня серьёзное недоумение - т.е. от языка текста зависит его разбитие на код поинты? Кто-нибудь может пояснить с минимальным примером желательно (на входе «ля-ля», в такой локали разбитие такое, в другой такое). Какие-то кодовые страницы на новом витке.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

И вот здесь у меня серьёзное недоумение - т.е. от языка текста зависит его разбитие на код поинты? Кто-нибудь может пояснить с минимальным примером желательно (на входе «ля-ля», в такой локали разбитие такое, в другой такое).

Вроде не по кодпоинтам, а по символам. Я не силён в терминологии, но есть как минимум две сущности - кодпоинт (он везде одинаковый, это 32битное число) и то что я назвал графемой выше (состоит из основного кодпоинта и комбинирующихся). Возможно есть ещё бОльшие или меньшие сущности, надо читать стандарт, и ты точно должен понимать по чему именно итерируется твой итератор.

А так примеров у меня особо нет, но для меня вполне очевидно что правила обработки строк зависят от локали (заметь, даже не от языка, локаль состоит из языка, местности, и, возможно дополнительных опций; в ru_RU и ru_UA могут быть разные правила (например, написания выражения «в/на Украину»)), и вообще в юникоде ничего простого и очевидного нет (но причина тому не в стандарте, а в язычках которые наплодило человечество). Например, есть же немецкая ß которая, будучи строчной, при приведении к uppercase станет SS. Если верно обратное, то это, очевидно, будет справедливо только для немецкого, в английском будет SSss и это всегда два отдельных символа. Что интересно, в 17 году они ввели строчную ß, так что в локаль по идее может входить ещё и дата.

Примеры можешь поискать в статьях а-ля «100500 фактов которые нужно знать о юникоде», на хабре как-то вроде была достаточно обширная статья. И на самом unicide.org довольно обширная документация с кучей примеров.

Какие-то кодовые страницы на новом витке.

При чём тут кодовые страницы? Кодовые страницы это просто набор символов в которые отображались байтовые значения 128..255. Юникод же про настоящие правила настоящих языков.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Вроде не по кодпоинтам, а по символам. Я не силён в терминологии, но есть как минимум две сущности - кодпоинт (он везде одинаковый, это 32битное число) и то что я назвал графемой выше (состоит из основного кодпоинта и комбинирующихся). Возможно есть ещё бОльшие или меньшие сущности, надо читать стандарт, и ты точно должен понимать по чему именно итерируется твой итератор.

Да, тут правильно говорить о графемах, не кодпоинтах. В любом случае - CharacterInstance самый «младший» из итераторов, но ходит не по кодпоинтам, по графемам, но как-то странно. Например, разбивает следуйщим образом (‘й’ записано в два кодпоинта ‘и’ с модификатором): ‘й’ | ‘ф’ | ‘ы’ | ‘в’ | ‘а’ | модификатор - ‘ударениe’ от ‘а’. Ну ладно, придётся и здесь резать модификаторы.

для меня вполне очевидно что правила обработки строк зависят от локали

Не согласен. Есть «самостоятельные» символы, есть модификаторы (вроде обзывают их no spacing символы). Никакой привязки к локали быть не должно. Я ведь не ради праздного интереса символы считаю, мне надо точно знать кол-во графем, сделать добивку/перенос. В теории, текст в софтину может прийти на любом языке и ответ в духе - графем будет X, а проитерероваться по этой строке можно X+-5 в зависимости от локали - никуда не годится.

При чём тут кодовые страницы?

Потому что надо таскать с собой какую-то метаинфу как со страницами. Звучит так, что буд-то я не могу корректно выводить текст на разных языках одновременно.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

Не согласен. Есть «самостоятельные» символы, есть модификаторы (вроде обзывают их no spacing символы). Никакой привязки к локали быть не должно.

Повторюсь, ß можно заuppercaseить либо в либо в SS в зависимости от локали. Явного примера про «считать» у меня нет, но не вижу почему там не может встретиться что-то подобное.

Я ведь не ради праздного интереса символы считаю, мне надо точно знать кол-во графем, сделать добивку/перенос. В теории, текст в софтину может прийти на любом языке и ответ в духе - графем будет X, а проитерероваться по этой строке можно X+-5 в зависимости от локали - никуда не годится.

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

Потому что надо таскать с собой какую-то метаинфу как со страницами. Звучит так, что буд-то я не могу корректно выводить текст на разных языках одновременно.

Естественно не можешь и естественно для этого нужна дополнительная разметка. Например:

https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

Повторюсь, ß можно заuppercaseить либо в ẞ либо в SS в зависимости от локали.

Это уже трансформация, это другое. Аналогично сравнение. В таких кейсах влияние локали логично.

Вот это уже конкретная чушь. Визуальный размер текста вообще нельзя считать по графемам …

Можно, сначала дробим на графемы, а после проверяем кол-во знакомест под каждую https://linux.die.net/man/3/wcswidth (она тоже зависит от локали, но верю, что локаль должна быть лишь unicode aware). Иначе никак, кто-то в конечном итоге на нижнем уровне должен отображать все эти лигатуры и прочее непотребство, и ему совсем небезразлична ширина графем.

kvpfs ★★
() автор топика

wstring str = L"фыва\u00b4"; // «фыва» с ударением на ‘а’

а потóм тебе на вход попадает текст с диактритикой вместо ударения потому что так было удобнее, и чего?

anonymous
()
Ответ на: комментарий от anonymous

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

а потóм тебе на вход попадает текст с диактритикой вместо ударения потому что так было удобнее, и чего?

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

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

диактрику буду резать

и с какой такой радости ты решил её резать, если ты весь из себя локаленезависимый?

anonymous
()

Ещё вопрос - а как обстоят дела с каким-нибудь справа налево письмом, например арабским? Можно ли поставить локаль, шрифт и в консоли получать записи справа налево или им приходится читать «наоборот», как все - слева направо?

Продумывать софт ещё и для разного направления письма - это явно слишком. Может у них началом координат в консоли считается верхний, правый угол (в ncurses, например, если она вообще такое умеет), и тогда они всё видят отзеркаленное (какие-нибудь граф построения)?

kvpfs ★★
() автор топика

Нет ли здесь ошибки?

// convets from [in_begin : in_end) to *o_out
void cvt(const wchar_t *in_begin,
		const wchar_t *in_end,
		std::basic_string<char> *o_out)
{
	using namespace std;
	auto& cvt = use_facet<codecvt<wchar_t, char, mbstate_t>>(locale());
	// expect SBO
	o_out->resize(15);
	mbstate_t mb{};
	const wchar_t *from_next = in_begin;
	char *to_next = &(*o_out)[0];
	while (cvt.out(mb, from_next, in_end, from_next,
			to_next, &(*o_out)[o_out->size()], to_next) ==
			std::codecvt_base::partial  &&
			from_next != in_end) {
		std::ptrdiff_t diff = to_next - &(*o_out)[0];
		o_out->resize(o_out->size() * 2);
		to_next = &(*o_out)[0] + diff;
	}
	o_out->resize(to_next - &(*o_out)[0]);
}

Меня смущает mbstate_t, я плохо понимаю зачем оно нужно. Я не нашёл ни одно примера, где бы кто-то повторно входил в cvt.out()

kvpfs ★★
() автор топика
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

Маленькая ß в немецком - в швейцарском диалекте всегда переводится в ss.

lovesan ★★
()
Ответ на: комментарий от dataman

О мой бог …

Т.е. ICU мне не поможет при сравнении строк? Хотелось бы - взять строку, сделать декомпозитинг, удалить всякое ненужное (какие-нибудь лигатуры и ударения), сделать композитинг (если что-то не представляется в композитном виде, то класть). Вроде так будет что-то более менее пригодное для регулярок. Особо ещё не вникал (пока занят корректной отрисовкой юникодного текста), но надеялся на что-то вроде такого из ICU:

Transliterator *accentsConverter = Transliterator::createInstance(
        "NFD; [:M:] Remove; NFC", UTRANS_FORWARD, status);

Собственный таблицы сопоставлений - это уже слишком, да и вряд ли сделаю правильно ввиду сложности и раздутости Юникода. Кирилллицу, конечно осилю, но как там не накосячить, когда на вход скормить какой-нибудь папуасинг - хз. Для меня оптимальней тогда остановиться на рабочем поиске лишь по ASCII, остальное - как повезёт - если композиция без мишуры, то норм.

kvpfs ★★
() автор топика
Ответ на: комментарий от kvpfs

Понятия не имею, как там в ICU, т.к. не использую её из-за монструозности.

Предпочитаю cpp-unicodelib или utf8proc.

Вроде так будет что-то более менее пригодное для регулярок.

Я как-то анонсировал regex-библиотеку SRELL, там фолдинг поддерживается «из коробки», вроде-бы.

dataman ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.