Как определить, принадлежит ли персонаж к языку справа налево?
Как правильно определить, содержит ли строка текст на языке справа налево.
Я нашел этот вопрос, который предлагает следующий подход:
public bool IsArabic(string strCompare)
{
char[] chars = strCompare.ToCharArray();
foreach (char ch in chars)
if (ch >= '\u0627' && ch <= '\u0649') return true;
return false;
}
Хотя это может работать для арабского языка, похоже, что оно не распространяется на другие языки RTL, такие как иврит. Есть ли общий способ узнать, что конкретный символ принадлежит к языку RTL?
4 ответа
Символы Юникода имеют различные свойства, связанные с ними. Эти свойства не могут быть получены из кодовой точки; вам нужна таблица, которая сообщает вам, есть ли у персонажа определенное свойство или нет.
Вас интересуют символы с двунаправленным свойством "R" или "AL" (RandALCat).
Символ RandALCat - это персонаж с однозначной направленностью справа налево.
Вот полный список Unicode 3.2 (из RFC 3454):
D. Двунаправленные таблицы D.1 Символы с двунаправленным свойством "R" или "AL" ----- Стартовая таблица D.1 ----- 05BE 05C0 05C3 05D0-05EA 05F0-05F4 061B 061F 0621-063A 0640-064A 066D-066F 0671-06D5 06DD 06E5-06E6 06FA-06FE 0700-070D 0710 0712-072C 0780-07A5 07B1 200F FB1D FB1F-FB28 FB2A-FB36 FB38-FB3C FB3E FB40-FB41 FB43-FB44 FB46-FBB1 FBD3-FD3D FD50-FD8F FD92-FDC7 FDF0-FDFC FE70-FE74 FE76-FEFC ----- Конечная таблица D.1 -----
Вот некоторый код, чтобы получить полный список с Unicode 6.0:
var url = "http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt";
var query = from record in new WebClient().DownloadString(url).Split('\n')
where !string.IsNullOrEmpty(record)
let properties = record.Split(';')
where properties[4] == "R" || properties[4] == "AL"
select int.Parse(properties[0], NumberStyles.AllowHexSpecifier);
foreach (var codepoint in query)
{
Console.WriteLine(codepoint.ToString("X4"));
}
Обратите внимание, что эти значения являются кодовыми точками Unicode. Строки в C#/.NET имеют кодировку UTF-16 и должны быть сначала преобразованы в кодовые точки Unicode (см. Char.ConvertToUtf32). Вот метод, который проверяет, содержит ли строка хотя бы один символ RandALCat:
static void IsAnyCharacterRightToLeft(string s)
{
for (var i = 0; i < s.Length; i += char.IsSurrogatePair(s, i) ? 2 : 1)
{
var codepoint = char.ConvertToUtf32(s, i);
if (IsRandALCat(codepoint))
{
return true;
}
}
return false;
}
Вы можете попробовать использовать " именованные блоки" в регулярных выражениях. Просто выберите блоки справа налево и сформируйте регулярное выражение. Например:
\p{IsArabic}|\p{IsHebrew}
Если это регулярное выражение возвращает true, то в строке был хотя бы один иврит или арабский символ.
Все "AL" или "R" Unicode 6.0 (с http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt)
bool hasRandALCat = 0;
if(c >= 0x5BE && c <= 0x10B7F)
{
if(c <= 0x85E)
{
if(c == 0x5BE) hasRandALCat = 1;
else if(c == 0x5C0) hasRandALCat = 1;
else if(c == 0x5C3) hasRandALCat = 1;
else if(c == 0x5C6) hasRandALCat = 1;
else if(0x5D0 <= c && c <= 0x5EA) hasRandALCat = 1;
else if(0x5F0 <= c && c <= 0x5F4) hasRandALCat = 1;
else if(c == 0x608) hasRandALCat = 1;
else if(c == 0x60B) hasRandALCat = 1;
else if(c == 0x60D) hasRandALCat = 1;
else if(c == 0x61B) hasRandALCat = 1;
else if(0x61E <= c && c <= 0x64A) hasRandALCat = 1;
else if(0x66D <= c && c <= 0x66F) hasRandALCat = 1;
else if(0x671 <= c && c <= 0x6D5) hasRandALCat = 1;
else if(0x6E5 <= c && c <= 0x6E6) hasRandALCat = 1;
else if(0x6EE <= c && c <= 0x6EF) hasRandALCat = 1;
else if(0x6FA <= c && c <= 0x70D) hasRandALCat = 1;
else if(c == 0x710) hasRandALCat = 1;
else if(0x712 <= c && c <= 0x72F) hasRandALCat = 1;
else if(0x74D <= c && c <= 0x7A5) hasRandALCat = 1;
else if(c == 0x7B1) hasRandALCat = 1;
else if(0x7C0 <= c && c <= 0x7EA) hasRandALCat = 1;
else if(0x7F4 <= c && c <= 0x7F5) hasRandALCat = 1;
else if(c == 0x7FA) hasRandALCat = 1;
else if(0x800 <= c && c <= 0x815) hasRandALCat = 1;
else if(c == 0x81A) hasRandALCat = 1;
else if(c == 0x824) hasRandALCat = 1;
else if(c == 0x828) hasRandALCat = 1;
else if(0x830 <= c && c <= 0x83E) hasRandALCat = 1;
else if(0x840 <= c && c <= 0x858) hasRandALCat = 1;
else if(c == 0x85E) hasRandALCat = 1;
}
else if(c == 0x200F) hasRandALCat = 1;
else if(c >= 0xFB1D)
{
if(c == 0xFB1D) hasRandALCat = 1;
else if(0xFB1F <= c && c <= 0xFB28) hasRandALCat = 1;
else if(0xFB2A <= c && c <= 0xFB36) hasRandALCat = 1;
else if(0xFB38 <= c && c <= 0xFB3C) hasRandALCat = 1;
else if(c == 0xFB3E) hasRandALCat = 1;
else if(0xFB40 <= c && c <= 0xFB41) hasRandALCat = 1;
else if(0xFB43 <= c && c <= 0xFB44) hasRandALCat = 1;
else if(0xFB46 <= c && c <= 0xFBC1) hasRandALCat = 1;
else if(0xFBD3 <= c && c <= 0xFD3D) hasRandALCat = 1;
else if(0xFD50 <= c && c <= 0xFD8F) hasRandALCat = 1;
else if(0xFD92 <= c && c <= 0xFDC7) hasRandALCat = 1;
else if(0xFDF0 <= c && c <= 0xFDFC) hasRandALCat = 1;
else if(0xFE70 <= c && c <= 0xFE74) hasRandALCat = 1;
else if(0xFE76 <= c && c <= 0xFEFC) hasRandALCat = 1;
else if(0x10800 <= c && c <= 0x10805) hasRandALCat = 1;
else if(c == 0x10808) hasRandALCat = 1;
else if(0x1080A <= c && c <= 0x10835) hasRandALCat = 1;
else if(0x10837 <= c && c <= 0x10838) hasRandALCat = 1;
else if(c == 0x1083C) hasRandALCat = 1;
else if(0x1083F <= c && c <= 0x10855) hasRandALCat = 1;
else if(0x10857 <= c && c <= 0x1085F) hasRandALCat = 1;
else if(0x10900 <= c && c <= 0x1091B) hasRandALCat = 1;
else if(0x10920 <= c && c <= 0x10939) hasRandALCat = 1;
else if(c == 0x1093F) hasRandALCat = 1;
else if(c == 0x10A00) hasRandALCat = 1;
else if(0x10A10 <= c && c <= 0x10A13) hasRandALCat = 1;
else if(0x10A15 <= c && c <= 0x10A17) hasRandALCat = 1;
else if(0x10A19 <= c && c <= 0x10A33) hasRandALCat = 1;
else if(0x10A40 <= c && c <= 0x10A47) hasRandALCat = 1;
else if(0x10A50 <= c && c <= 0x10A58) hasRandALCat = 1;
else if(0x10A60 <= c && c <= 0x10A7F) hasRandALCat = 1;
else if(0x10B00 <= c && c <= 0x10B35) hasRandALCat = 1;
else if(0x10B40 <= c && c <= 0x10B55) hasRandALCat = 1;
else if(0x10B58 <= c && c <= 0x10B72) hasRandALCat = 1;
else if(0x10B78 <= c && c <= 0x10B7F) hasRandALCat = 1;
}
}
РЕДАКТИРОВАТЬ:
Это то, что я сейчас использую, оно включает в себя символы гласности и все на иврите и арабском:
[\u0591-\u07FF]
СТАРЫЙ ОТВЕТ:
Если вам нужно определить язык RTL в предложении, этого упрощенного RegEx, вероятно, будет достаточно:
[א-ת-ۿ]
Если кто-то хочет написать что-то на иврите, ему придется использовать один из этих символов, и случай аналогичен арабскому.
Он не включает в себя символы гласности, поэтому, если вам нужно поймать все целые слова или абсолютно все символы RTL, вам лучше использовать один из других ответов. Символы гласности на иврите очень редко встречаются в не поэтических текстах. Я не знаю об арабских текстах.
В моей реализации регулярного выражения я не мог использовать ни \u, \x, ни {} языковые группы с именами.
Поэтому я построил свой собственный шаблон программно на основе всех двунаправленных символов "R" и "AL" (RandALCat), перечисленных в UnicodeData.txt.
[־׀׃א-״؛-ي٭-ەײַ-ﳝﶈ-ﷺﺂ-ﻼ]
Это должно быть достаточно всеобъемлющим, и я пока тестировал его на арабском и иврите.