Выбор символа разделителя тысяч с помощью RegEx - PullRequest
5 голосов
/ 01 июня 2019

Мне нужно изменить десятичный разделитель в заданной строке, в которой есть числа.

Какой код RegEx может ТОЛЬКО выбрать символ разделителя тысяч в строке?

Его нужно выбирать только тогда, когда вокруг него есть номер. Например, только когда 123,456 мне нужно выбрать и заменить ,

Я конвертирую английские цифры в персидские (например: Hello 123 становится Hello ۱۲۳). Теперь мне нужно заменить десятичный разделитель на персидскую версию. Но я не знаю, как я могу выбрать его с помощью регулярных выражений. например Hello 121,534 большинство становятся Hello ۱۲۱/۵۳۴

Символ, который необходимо заменить, - , на /

Ответы [ 4 ]

5 голосов
/ 01 июня 2019

Согласно вашему вопросу, основная проблема, с которой вы сталкиваетесь, заключается в преобразовании английского номера в персидский.

В 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 чаще всего не является ответом.Я почти уверен, что есть регулярные выражения, которые могут дать вам одну строку, которая довольно стабильна, но контекст ее использования не будет очень стабильным.Однако не говоря о том, что есть только один ответ.Вместо этого объединение различных уровней действий (разделяй и властвуй) позволяет рассчитывать на стабильное преобразование чисел, даже если вы все еще не уверены в том, как создать регулярное выражение для английского числа.

5 голосов
/ 01 июня 2019

Используйте регулярное выражение с lookarounds.

$new_string = preg_replace('/(?<=\d),(?=\d)/', '/', $string);

DEMO

(?<=\d) означает, что перед запятой должна быть цифра, (?=\d) означает, что после нее должна быть цифра. Но так как это обходные пути, они не включены в матч, поэтому они не заменяются.

0 голосов
/ 01 июня 2019

Вы можете написать регулярное выражение для захвата чисел с разделителем тысяч, а затем объединить две числовые части с нужным разделителем:

$text = "Hello, world, 121,534" ;
$pattern = "/([0-9]{1,3}),([0-9]{3})/" ;
$new_text = preg_replace($pattern, "$1X$2", $text); // replace comma per 'X', keep other groups intact.

echo $new_text ; // Hello, world, 121X534
0 голосов
/ 01 июня 2019

В PHP вы можете сделать это, используя str_replace

$a="Hello 123,456";
echo str_replace(",", "X", $a);

Это вернет: Hello 123X456

...