как с помощью регулярного выражения найти вхождения в которых цифра больше нуля
есть множество строк
_balance":0
_balance":7
_balance":0
_balance":0.1
_balance":0
_balance":0
стоит задача найти строки в которых balance больше нуля
из того что я придумал
_balance":[^0]
не подходит для дробных чисел
Ответы (3 шт):
Дисклеймер:
Я не grep-guru и не могу похвастаться лаконичностью своих регулярок.
В качестве более лаконичного ответа советую вариант Андрея
Но что-то работающее предложить могу и я.
Как и объяснить что там к чему.
Также интересным может быть вариант ipatev_nn с опережающими и ретроспективными проверками
Замечание #1:
Как правильно заметил @Laukhin Andrey, символьный класс [^0] будет содержать абсолютно все символы кроме нуля, в том числе буквы и пр. символы. Поэтому вместо него лучше использовать [1-9]
К решению:
В регулярном выражении можно использовать символ |, который работает как оператор ИЛИ и позволяет выбрать одно из двух выражений, находящихся справа и слева от него.
В нашем случае мы выбираем
- целые больше нуля
т.к. числа могут иметь более одного разряда условие на отсутствие нуля оставляем только для первого, при этом второй и дальнейшие разряды опциональны
Запишем это следующим образом:[1-9]\d{0,}
[1-9]- первый символ находится в последовательности от 0 до 9
\d{0,}- второй и последующие являются числами и количество их может быть 0 и более (поэтому мы не указываем верхнюю границу в квантификаторе{0,}) - дробные с целой частью равной нулю
Записываем следующим образом:0\.\d+ - незабываем про дробные с целой частью больше нуля
[1-9]\d{0,}\.\d+
Также нужно будет воспользоваться группировкой т.к. мы не хотим неодназначности при поиске регулярных выражений и не хотим чтобы условие ИЛИ нечаянно включило бы _balance в левую часть или же отработало бы по части регулярного выражения.
Поэтому отделяем мухи от котлет, а строковые ключи от численных значений:
_balance":(внутри группировки условие поиска численного значения)
и обозначаем границы выбора:
(целые больше нуля)|(дробные с целой частью равной нулю )|(дробные с целой частью больше нуля)
Также можно указать, что мы будем искать подходящее выражение до конца строки, чтобы приложение не расслаблялось находя минимально необходимую часть.
Для этого добавим в конце регулярного выражения символ $
Применим все вышеописанное и подставим все на свои места
_balance":(([1-9]\d{0,})|(0\.\d+)|([1-9]\d{0,}\.\d+))$
Замечание #2:
Также @Владимир Клыков совершенно верно обратил внимание на то, что выражение _balance":(([1-9]\d{0,})|(0\.\d+)|([1-9]\d{0,}\.\d+))$ также будет реагировать на 0.0.
Данный недостаток я заметил еще на стадии написания, но в конкретном кейсе я предпочел принебречь данным обстоятельством, чтобы не раздувать и без того немаленькую регулярку.
Так я решил, потому что строка по которой мы ищем совпадения является выхлопом слаботипизированного языка(такого как php и пр.), в котором 0.0 автоматически будет приводиться к 0 и данное условие в таком случае можно опустить.
Но для других пользователей стоит это учесть.
Проблема в \d+, которое может содержать и ноль тоже.
Можно топорно решить добавив еще одно вложенное условие
и заменить \d+ в (0\.\d+) на ([1-9]{1}|\d{2,})
_balance":(([1-9]\d{0,})|(0\.([1-9]{1}|\d{2,}))|([1-9]\d{0,}\.\d+))$
либо же, как Laukhin Andrey двигаться в сторону условия наличия хотя бы одного ненулевого символа и опциональном наличии точки.
_balance":([1-9]\d*\.?\d*|\d\.\d*[1-9]\d*)
Либо первая цифра от 1 до 9, а за ней любые цифры (допуская точку), либо первая цифра 0, тогда мы ожидаем точку, а за ней должна быть хотя бы одна ненулевая цифра.
Так как ЯП не указан, приведу пример для JS:
(?<![\d.-])(0\.\d*[1-9]\d*|\d*[1-9]\d*(?:\.\d+)?)(?![\d.-])
const regex = /(?<![\d.-])(0\.\d*[1-9]\d*|\d*[1-9]\d*(?:\.\d+)?)(?![\d.-])/gm;
const str = `_balance":0
_balance":7
_balance":0.0010
_balance":0.1
_balance":0.0
_balance":00
_balance":01
_balance":02.13
_balance":1234.1234
_balance":00.13
_balance":-1`;
console.log(str.match(regex));
(?<![\d.-]) - негативный просмотр назад, где нет символа цифры, точки или тире
(0\.\d*[1-9]\d*|...) - если вначале 0. то проверяем что бы в цифрах после точки была хоть одна отличная от нуля
(...|\d*[1-9]\d*(?:\.\d+)?) - в случае если цифра до точки выше нуля, то необязательной группой будет является часть после точки, т.е. 1.01 и 1 будут считаться валидными
(?![\d.-]) - негативный просмотре вперед что символ после захвата не будет являться цифре, точке или тире.
