Выражение регулярного выражения, чтобы помочь подсчитать только нули в строке - PullRequest
0 голосов
/ 31 октября 2011

Я пытаюсь посчитать количество 0 в строке чисел. Не только символ 0, но и ноль. например Я хочу сосчитать 0, 0.0, 0.000 и т. Д. Числа будут разделены пробелами, например ::10000

1.0 5.0 1 5.4 12 0.1 14.2675 0.0 0.00005

Простой поиск " 0" в строке почти делает работу (для этого нужно сначала вставить начальный пробел в строке - в случае, если первое число равно нулю). Однако это не работает для чисел в форме 0.x например. 0.1, 0.02 и т. Д. Я полагаю, мне нужно проверить на 0 и посмотреть, есть ли после нее десятичная точка и затем ненулевые числа, но я понятия не имею, как это сделать. Что-то вроде:

" 0*|(0\\.(?!\\[1-9\\]))"

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

ПРИМЕЧАНИЕ: я использую split в Java для этого (разбить строку, используя регулярное выражение, а затем считать с помощью .length()).

Ответы [ 3 ]

3 голосов
/ 31 октября 2011

Как насчет этого:

(?<=^|\s)[0.]+(?=\s|$)

Объяснение:

(?<=^|\s) # Assert position after a space or the start of the string
[0.]+     # Match one or more zeroes/decimal points
(?=\s|$)  # Assert position before a space or the end of the string

Не забудьте удвоить обратную косую черту в строках Java.

2 голосов
/ 31 октября 2011

Вместо этого следует разделить пробелами и использовать Double.parseDouble () для каждого фрагмента, затем, если он действительно является двойным, сравнить его с 0.

String[] parts = numbers.split("\\s+");
int numZeros = 0;
for (String s: parts) {
    try {
        if (Double.parseDouble(s) == 0) {
            numZeros ++;
        }
    } 
    catch (Exception e) {
    }
}

В любом случае, для регулярного выражения нет простого решения. Самой простой мыслью было бы использовать граничный оператор \ b, но он плохо работает. Кроме того, Double.parseDouble означает, что такие вещи, как -0, также поддерживаются.

1 голос
/ 31 октября 2011

split() не является решением этой проблемы, хотя, как продемонстрировал ответ Антти, оно может быть частью решения. Вам будет намного проще сопоставлять числа с нулевым значением в цикле find() и подсчитывать совпадения следующим образом:

String s = "1.0 5.0 1 5.4 12 0.1 14.2675 0.0 0.00005 0. .0 0000 -0.0";

Pattern p = Pattern.compile("(?<!\\S)-?(?:0+(?:\\.?0*)|\\.0+)(?!\\S)");
Matcher m = p.matcher(s);
int n = 0;

while (m.find()) {
    System.out.printf("%n%s ", m.group());
    n++;
}
System.out.printf("%n%n%d zeroes total%n", n);

выход:

0.0
0.
.0
0000
-0.0

5 zeroes total

Вот так Тим и хотел, чтобы вы тоже использовали регулярное выражение в своем ответе (я думаю). Разбивая мое регулярное выражение, мы имеем:

  • (?<!\\S) - это отрицательный вид сзади, который соответствует позиции, которой не предшествует непробельный символ. Это эквивалентно положительному взгляду Тима, (?<=^|\s), который явно соответствует началу строки или сразу после пробела.

  • -?(?:0+(?:\\.?0*)|\\.0+) соответствует необязательному знаку минус, за которым следует хотя бы один ноль и не более одного десятичного знака.

  • (?!\\S) эквивалентно (?=\s|$) - оно совпадает непосредственно перед символом пробела или в конце строки.

Взгляд назад и взгляд вперед гарантируют, что вы всегда будете соответствовать целому токену, как если бы вы делились на пустое пространство. Без них он также будет соответствовать нулям, которые являются частью ненулевых токенов, таких как 1230.0456.


РЕДАКТИРОВАТЬ (в ответ на комментарий): Мое основное возражение против использования split() заключается в том, что он излишне запутан. Вы создаете массив строк, включающий в себя все части строки, которые вас не интересуют, а затем выполняете некоторые вычисления по длине массива, чтобы получить необходимую информацию. Конечно, это всего лишь одна строка кода, но он очень плохо сообщает о своих намерениях. Любой, кто еще не знаком с этой идиомой, может очень трудно разобраться, что она делает.

Тогда возникает проблема с висячими пустыми токенами: если вы используете технику разделения на моей пересмотренной строке примера, вы получите счетчик 4, а не 5. Это связано с тем, что последний фрагмент строки соответствует разделенному регулярному выражению, то есть последний токен должен быть пустой строкой. Но Java (следуя примеру Perl) по умолчанию молча отбрасывает конечные пустые токены. Вы можете переопределить это поведение, передав отрицательное целое число в качестве второго аргумента, но что, если вы забудете это сделать? Это очень простая ошибка, и потенциально очень трудная для устранения.

Что касается производительности, два подхода практически идентичны по скорости (я не знаю, какую память они используют). Это вряд ли будет проблемой при работе с текстами разумного размера.

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