Согласно вашему вопросу, основная проблема, с которой вы сталкиваетесь, заключается в преобразовании английского номера в персидский.
В PHP есть библиотека, которая может форматировать и анализировать номера в соответствии с локалью, вы можете найтиэто в классе NumberFormatter , который использует хранилище данных Unicode Common Locale (CLDR) для обработки - в конце концов - всех известных в мире языков.
Таким образом, преобразование числа 123,456
из en_UK
(или en_US
) в fa_IR
показано в этом небольшом примере:
$string = '123,456';
$float = (new NumberFormatter('en_UK', NumberFormatter::DECIMAL))->parse($string);
var_dump(
(new NumberFormatter('fa_IR', NumberFormatter::DECIMAL))->format($float)
);
Вывод:
string(14) "۱۲۳٬۴۵۶"
( играть с ним на 3v4l.org )
Теперь это показывает (каким-то образом), как конвертировать число.Я не очень тверда с персидским, так что извините, если я использовал здесь не ту локаль.Также могут быть варианты, чтобы указать, какой символ использовать для группировки, но на данный момент для примера это просто показывает, что преобразование чисел осуществляется существующими библиотеками.Вам не нужно заново изобретать это, что является даже своего рода неправильной формулировкой, это не что-то, что мог бы сделать один человек, или, по крайней мере, было бы безумно делать это в одиночку.
Итак, после выяснения того, как преобразовать эти числа, остается вопрос, как это сделать для всего текста.Ну, почему бы не найти все потенциальные места, которые ищут, а затем попытаться проанализировать совпадение и в случае успеха (и только в случае успеха) преобразовать его в другую локаль.
К счастью, NumberFormatter::parse()
метод возвращает значение false, если синтаксический анализ завершился неудачно (в случае, если вас интересуют более подробные сведения, появляется еще больше сообщений об ошибках), так что это работоспособно.
Для сопоставления с регулярным выражением требуется только шаблон, соответствующий числу (наибольшее совпадение выигрывает), и замена может быть сделана обратным вызовом.В следующем примере перевод выполняется многословно, поэтому фактический синтаксический анализ и форматирование более заметны:
# some text
$buffer = <<<TEXT
it need to only select , when there is number around it. for example only
when 123,456 i need to select and replace "," I'm converting English
numbers into Persian (e.g: "Hello 123" becomes "Hello ۱۲۳"). now I need to
replace the Decimal separator with Persian version too. but I don't know how
I can select it with regex. e.g: "Hello 121,534" most become
"Hello ۱۲۱/۵۳۴" The character that needs to be replaced is , with /
TEXT;
# prepare formatters
$inFormat = new NumberFormatter('en_UK', NumberFormatter::DECIMAL);
$outFormat = new NumberFormatter('fa_IR', NumberFormatter::DECIMAL);
$bufferWithFarsiNumbers = preg_replace_callback(
'(\b[1-9]\d{0,2}(?:[ ,.]\d{3})*\b)u',
function (array $matches) use ($inFormat, $outFormat) {
[$number] = $matches;
$result = $inFormat->parse($number);
if (false === $result) {
return $number;
}
return sprintf("< %s (%.4f) = %s >", $number, $result, $outFormat->format($result));
},
$buffer
);
echo $bufferWithFarsiNumbers;
Вывод:
it need to only select , when there is number around it. for example only
when < 123,456 (123456.0000) = ۱۲۳٬۴۵۶ > i need to select and replace "," I'm converting English
numbers into Persian (e.g: "Hello < 123 (123.0000) = ۱۲۳ >" becomes "Hello ۱۲۳"). now I need to
replace the Decimal separator with Persian version too. but I don't know how
I can select it with regex. e.g: "Hello < 121,534 (121534.0000) = ۱۲۱٬۵۳۴ >" most become
"Hello ۱۲۱/۵۳۴" The character that needs to be replaced is , with /
Здесь волшебство состоит только из двух, приводящих части строки вдействовать с преобразованием чисел, используя preg_replace_callback
с шаблоном регулярного выражения, который должен соответствовать потребностям в вашем вопросе, но относительно легко уточнить, так как вы определяете целую часть числа, и ложные срабатывания фильтруются благодаря NumberFormatter класс:
pattern for Unicode UTF-8 strings
|
(\b[1-9]\d{0,2}(?:[ ,.]\d{3})*\b)u
| | |
| grouping character |
| |
word boundary -----------------+
( играть с ним на regex101.com )
Редактировать:
Комусопоставлять только один и тот же группирующий символ в нескольких тысячах блоков, можно создать именованную ссылку и сослаться на нее для повторения:
(\b[1-9]\d{0,2}(?:(?<grouping_char>[ ,.])\d{3}(?:(?&grouping_char)\d{3})*)?\b)u
(теперь этот метод становится менее читаемым, получить его расшифрованныйи поиграйте с ним на regex101.com )
Чтобы завершить ответ, нужно только сжать условие возврата до return $outFormat->format($result);
и $outFormat
NumberFormatter может потребоваться дополнительная настройка, но, поскольку она доступна в замыкании, это можно сделать при ее создании.
( играть с ним на 3v4l.org )
Я надеюсь, что это полезно и открывает более широкую картину, чтобы не искать решения только из-за удара о стену (и только там).Один Regex чаще всего не является ответом.Я почти уверен, что есть регулярные выражения, которые могут дать вам одну строку, которая довольно стабильна, но контекст ее использования не будет очень стабильным.Однако не говоря о том, что есть только один ответ.Вместо этого объединение различных уровней действий (разделяй и властвуй) позволяет рассчитывать на стабильное преобразование чисел, даже если вы все еще не уверены в том, как создать регулярное выражение для английского числа.