Нещодавно @javascript показав мені що окрім банального екранування данних потрібно думати ще і про UTF8 символи, якими можно надати тексту кастомний вигляд.
Не зміг знайти точної назви для такої техніки, але на сайтах-генераторах це звесться “текст символами”, або “генерація надписів із символів”, або “ASCII art”.
Для прикладу наведу один з генераторів:
До певного моменту ці символи виглядають прикольно, але коли ними зловживати то це ломає зовнішній вид інтерфейсу та створює враження що сайт зламаний.
Приклад:
Як боротися з ASCII-артом, якщо символи ломають зовнішний вигляд?
Правильна відповідь – треба обмежити використання UTF-8 символів. Залишити лише символи, які входять до алфафіту мови, цифри, пунктуацію та емоджі.
Можна використати регулярні вирази – ідеальний інструмент для роботи з текстом.
Задача виглядає простою. Але коли я почав експерементувати з текстом, виявилося що треба додати в “білий список” абсалютно всі символи котрими я користуюся, а ще потенційно багато симіолів про які я й не здогадуюсь, але вони все одно потрібні. Це забагацько роботи.
На допомогу приходять спеціальні Regex-групи UTF-8 символів. Для зручності симфоли згрупували та надали їм назви, тож їх тепер можно не описувати в Regex-виразі посимвольно, а просто вказати назву групи.
Які є групи Regex-символів?
Є основні групи UTF-8 символів котрі ще діляться на підгрупи. Далі йде список, який складається з двох рівнів. Перший рівень – велика група символів, підрівень – підгрупа символів.
\p{L}або\p{Letter}: будь-яка літера з будь-якої мови.\p{Ll}або\p{Lowercase_Letter}: мала літера, яка має варіант з великою.\p{Lu}або\p{Uppercase_Letter}: велика літера, яка має варіант малої.\p{Lt}або\p{Titlecase_Letter}: літера, яка з'являється на початку слова, коли з великої літери пишеться лише перша літера слова.\p{L&}або\p{Cased_Letter}: літера, яка існує у малих та великих регістрах (комбінація Ll, Lu та Lt).\p{Lm}або\p{Modifier_Letter}: спеціальний символ, який використовується як літера.\p{Lo}або\p{Other_Letter}: літера або ідеограма, яка не має малих і великих літер.
\p{M}або\p{Mark}: символ, призначений для поєднання з іншим символом (наприклад, наголоси, умляути, поля, що закривають, тощо).\p{Mn}або\p{Non_Spacing_Mark}: символ, призначений для поєднання з іншим символом без зайвого простору (наприклад, наголоси, умляути тощо).\p{Mc}або\p{Spacing_Combining_Mark}: символ, призначений для поєднання з іншим символом, який займає додатковий простір (голосні знаки у багатьох східних мовах).\p{Me}або\p{Enclosing_Mark}: символ, що охоплює символ, з яким він поєднується (коло, квадрат, клавіша тощо).
\p{Z}або\p{Separator}: будь-який пробіл або невидимий роздільник.\p{Zs}або\p{Space_Separator}: пробіл, який є невидимим, але займає місце.\p{Zl}або\p{Line_Separator}: символ-розділювач рядка U+2028.
\p{Zp}або\p{Paragraph_Separator}: символ-розділювач абзацу U+2029.
\p{S}або\p{Symbol}: математичні символи, грошові знаки, динбати, символи для малювання коробок тощо.\p{Sm}або\p{Math_Symbol}: будь-який математичний символ.\p{Sc}або\p{Currency_Symbol}: будь-який знак валюти.\p{Sk}або\p{Modifier_Symbol}: комбінований символ (знак) як повноцінний символ.\p{So}або\p{Other_Symbol}: різні символи, які не є математичними символами, знаками валют або комбінованими символами.
\p{N}або\p{Number}: будь-який цифровий символ у будь-якому шрифті.\p{Nd}або\p{Decimal_Digit_Number}: цифра від нуля до дев'яти будь-яким шрифтом, крім ідеографічних.\p{Nl}або\p{Letter_Number}: число, яке виглядає як літера, наприклад, римська цифра.\p{No}або\p{Other_Number}: надрядкова або підрядкова цифра, або число, яке не є цифрою 0-9 (за винятком чисел з ідеографічних шрифтів).
\p{P}або\p{Punctuation}: будь-який знак пунктуації.\p{Pd}або\p{Dash_Punctuation}: будь-який дефіс або тире.\p{Ps}або\p{Open_Punctuation}: будь-яка відкриваюча дужка.\p{Pe}або\p{Close_Punctuation}: будь-який тип закриваючої дужки.\p{Pi}або\p{Initial_Punctuation}: будь-який тип початкової лапки.\p{Pf}або\p{Final_Punctuation}: будь-яка закриваюча дужка.\p{Pc}або\p{Connector_Punctuation}: розділовий знак, наприклад, підкреслення, який з'єднує слова.\p{Po}або\p{Other_Punctuation}: будь-який розділовий знак, який не є тире, дужкою, лапкою або з'єднувачем.
\p{C}або\p{Other}: невидимі контрольні символи і невикористані кодові точки.\p{Cc}або\p{Control}: контрольний символ ASCII або Latin-1: 0x00-0x1F і 0x7F-0x9F.\p{Cf}або\p{Format}: невидимий індикатор форматування.\p{Co}або\p{Private_Use}: будь-яка точка коду, зарезервована для приватного використання.\p{Cs}або\p{Surrogate}: половина сурогатної пари у кодуванні UTF-16.\p{Cn}або\p{Unassigned}: будь-яка кодова точка, якій не призначено жодного символу.
Ще є додаткові групи, які спецефічні до конкретних мов, повний список можно переглянути на сторінці:
А як бути з Emoji? Як написати regex який буде відловлювати смайлики?
Нажаль, спеціальної regex-групи для Emoji поки не створено. В окремих реалізаціях Regex такі групи існують, але в PHP нажаль ні.
Тому нам потрібно самостійно описати номери UTF-8 символів які потім перетворяться на Emoji.
Я загуглив ці послідовності і просто залишу їх тут:
\x{1F600}-\x{1F64F}\x{2700}-\x{27BF}\x{1F680}-\x{1F6FF}\x{24C2}-\x{1F251}\x{1F30D}-\x{1F567}\x{1F900}-\x{1F9FF}\x{1F300}-\x{1F5FF}
Повний Regex для очистки тексту від небажаних UTF-8 символів виглядає так (код на PHP):
function sanitizeString(string $string) {
return preg_replace('/[^\p{L}\p{Z}\p{S}\p{N}\p{P}\p{C}\x{1F600}-\x{1F64F}\x{2700}-\x{27BF}\x{1F680}-\x{1F6FF}\x{24C2}-\x{1F251}\x{1F30D}-\x{1F567}\x{1F900}-\x{1F9FF}\x{1F300}-\x{1F5FF}]/u', '', $string);
}
Цей регулярний вираз залишає наступні символи:
- будь-які літери з будь-якої мови,
- будь-які пробіли або невидимі роздільники,
- математичні символи, грошові знаки, динбати, символи для малювання коробок тощо,
- будь-які цифрові символи у будь-якому шрифті,
- невидимі контрольні символи і невикористані кодові точки,
- будь-які знаки пунктуації,
- емоджі.
Інші символи вирізаються.
Висновок
Я розказав про актуальну проблему з UTF-8 символами, які ломають відображення тексту. Ми розібрали які групи UTF-8 символів існують, а на прикінці я поділився регулірним виразом для очистки UTF-8 текста від небажаних символів.
Сподіваюся, статя була інформативною і у вас є запитання – з ними у коментарі.
Вау, дуже прикольно
тож буквально хвилину тому у шапці було якесь непунятне "privet", а зараз вже виправили, не?
@lemmy там багато всього, для прикладу bezpecnim можна ж було написати через ch, а vidobrazennia через zh, це давно вже використовується, то дійсно якийсь дивний конвертор
@lemmy брешу, наведу, раніше призвища, яки закінчувалися на "ий" писалися як "yy" типу "gorobetskyy", але за новими правилами пишуть "yi" як "gorobetskyi", що технічно (посимвольно) має читатися як "горобецькиі"
@triqnot Якщо брати трансліт який транслітерує паспортні дані, то такий трансліт певно має зворотнью конвертацію. Звідти я і дізнався про те як траслітирувати "привіт". Але той же самий трансліт, який використовується на цьому сайті (генерує Slug з заголовку) – транслітерує як "privit" (перевірив). Можливо, треба його замінити на більш точний.
@lemmy прикладів не наведу, але не виключаю можливих проблем у складних випадках, але ж тут +- усе однозначно
@triqnot Якщо не помиляюся, трансліти не завжди мають властивість зворотньої конвертації. Часто бачив трансліти які зʼїдали символи, або використовували одні й ті самі символи для споріднених звуків. Тож логіка тут не спрацювала.
@himarstime я шось не вкурюю, ніби хештег "Kyiv not Kiev" пройшов повз усю комьюніті
@lemmy але ж в цьому випадку мала би спрацювати програмістська логіка, бо якщо "і" передається як "і" і "и" також як "і", то як тоді розрізняти при читанні транслітерації?
@triqnot Я подумав, що "и" англійською записується як "i". Але пошукав трансліти – дійсно для "и" є інша транслітирація. Правильно буде "pryvit". Не знав, запам'ятаю.
@triqnot дай англійську літеру и
@lemmy та я тікі хтів почати скаржитись. Але ж ми не кажимо "прівіт", чи то якись Український Творчий Формат, скорочено УТФ?
@triqnot Спіймали мене! Так, здалося що можно зробити кращу картинку. :)
@javascript Для тебе є інформація. :)