Группировка номеров с помощью регулярных выражений - PullRequest
3 голосов
/ 16 декабря 2009

Можно ли группировать числа (например, преобразовать число 1000 в строку "1 000"), используя один проход с только регулярными выражениями ? (Я знаю, что границы между regexp и языковыми возможностями в некоторых системах немного размыты - прислушайтесь к своей совести, прежде чем отвечать.)

Причина, по которой я спрашиваю: недавно другой разработчик спросил меня, как сделать группировку чисел в JavaScript, и показал мне немного некорректную функцию JavaScript с использованием регулярных выражений. Я дал ему лучшую альтернативу , но его регулярное выражение не давало мне покоя, потому что этот вид переписывания определенно то, что должна делать обычная грамматика, но я действительно не могу понять, как написать регулярное выражение для Это.

Это моя первая наивная попытка, которая, как я знал, будет неверной:

function group(n) { return n.toString().replace(/(\d{3})/g, "$1 "); }

У этого подхода есть два недостатка; group(1000) дает "100 0", а group(100) дает "100 " (завершающий пробел). Вы можете исправить это так:

String.prototype.reverse = function () { 
    var a = []; 
    for (var i = this.length; i >= 0; --i) a.push(this[i]); 
    return a.join("");
}; 
function group(n) { 
    return n.toString().reverse().replace(/(\d{3})/g, "$1 ").
        trimRight().reverse(); 
}

Но для этого требуется не один, не два, даже не три, а ЧЕТЫРЕ прохода (два реверса, один заменитель и trimRight)! Затем я отважился выйти на землю, где можно было наблюдать, и придумал:

function group(n) { return n.toString().replace(/(\d{3}(?!\d))/g, " $1");

... который вообще не работает ( редактировать - возможно, потому что я перепутал просмотр назад и отрицательный просмотр вперед ... ) - он соответствует только трем последним цифрам (group(1000000000) становится "1000000 000"). Предварительный просмотр работает немного лучше:

function group(n) { return n.toString().replace(/(\d{3})(?=\d)/g, "$1 "); }

Что более или менее возвращает меня туда, откуда я начал - я избавился от конечного пробела, но group(1000) все еще дает "100 0".

Итак, можно ли это сделать с помощью одного прохода замены регулярного выражения? Я не зависим от языка, так как для этого нужно использовать только регулярные выражения.

Примечание: это не вопрос о том, как сделать локализацию, и я не участвую в преждевременной оптимизации. Мне просто любопытно, возможно ли это, а если нет, то почему бы и нет.

Ответы [ 3 ]

8 голосов
/ 16 декабря 2009

Вот версия, которая будет работать на JavaScript:

return n.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1 ");
5 голосов
/ 16 декабря 2009

Это делается в Perl:

$num =~ s/(?<=\d)(\d{3})(?=(\d{3})*(\D|$))/ $1/g;

Чтобы разбить его:

  • (?<=\d) - мы проверяем, что нашему совпадению предшествует цифра с использованием взгляда назад

  • (\d{3}) - мы ищем группу из трех цифр

  • (?= - мы используем прогноз, поэтому за тремя цифрами должно следовать что-то

  • (\d{3})* - Это будет соответствовать 0 или более групп из 3 цифр, то есть 0, 3, 6 ... цифр.

  • (\D|$) - Это будет соответствовать не цифре или концу строки.

Итак, мы хотим найти цифру, за которой следуют 3 цифры, затем 0, 3, 6 ... цифр и затем никаких цифр.

К сожалению, JavaScript не имеет заглядывания в свои регулярные выражения, поэтому этот шаблон не будет работать в JavaScript. Если вы бросите взгляд назад, вы получите начальный пробел, поставленный перед цифрами с 3, 6, 9 ... цифрами.

4 голосов
/ 16 декабря 2009
n.toString().replace(/(\d)(?=(\d{3})+\b)/g,"$1 ")

Добавьте пробел после каждой цифры, за которой следуют 3i цифры. Например, в 123456789 эти цифры будут совпадать: 2, 6.
Рабочая демоверсия: http://jsbin.com/iruzu

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...