Regex, чтобы найти слова с буквами и цифрами, разделенными или не символами - PullRequest
2 голосов
/ 20 апреля 2011

Мне нужно построить регулярное выражение, которое сопоставляет слова с этими шаблонами:

Буквы и цифры:

A35, 35A, B503X, 1ABC5

Буквы и цифры, разделенные "-", "/", "\":

AB-10, 10-AB, A10-BA, BA-A10 и т.д ...

Я написал это регулярное выражение для него:

\b[A-Za-z]+(?=[(?<!\-|\\|\/)\d]+)[(?<!\-|\\|\/)\w]+\b|\b[0-9]+(?=[(?<!\-|\\|\/)A-Za-z]+)[(?<!\-|\\|\/)\w]+\b

Он работает частично, но он соответствует только буквам или только цифрам, разделенным символами. Пример:

10-10, open-office и др.

И я не хочу этого совпадения.

Я думаю, что мое регулярное выражение очень повторяющееся и несколько уродливое. Но это то, что у меня есть сейчас.

Может ли кто-нибудь мне помочь?

Я использую Java / Groovy.

Заранее спасибо.

Ответы [ 5 ]

6 голосов
/ 20 апреля 2011

Интересный вызов. Вот Java-программа с регулярным выражением, которая выбирает типы «слов», которые вы ищете:

import java.util.regex.*;
public class TEST {
    public static void main(String[] args) {
        String s = "A35, 35A, B503X, 1ABC5 " +
            "AB-10, 10-AB, A10-BA, BA-A10, etc... " +
            "10-10, open-office, etc.";
        Pattern regex = Pattern.compile(
            "# Match special word having one letter and one digit (min).\n" +
            "\\b                       # Match first word having\n" +
            "(?=[-/\\\\A-Za-z]*[0-9])  # at least one number and\n" +
            "(?=[-/\\\\0-9]*[A-Za-z])  # at least one letter.\n" +
            "[A-Za-z0-9]+              # Match first part of word.\n" +
            "(?:                       # Optional extra word parts\n" +
            "  [-/\\\\]                # separated by -, / or //\n" +
            "  [A-Za-z0-9]+            # Match extra word part.\n" +
            ")*                        # Zero or more extra word parts.\n" +
            "\\b                       # Start and end on a word boundary", 
            Pattern.COMMENTS);
        Matcher regexMatcher = regex.matcher(s);
        while (regexMatcher.find()) {
            System.out.print(regexMatcher.group() + ", ");
        } 
    }
}

Вот правильный вывод:

A35, 35A, B503X, 1ABC5, AB-10, 10-AB, A10-BA, BA-A10,

Обратите внимание, что единственными сложными регулярными выражениями, которые являются "безобразными", являются те, которые неправильно отформатированы и закомментированы!

1 голос
/ 21 апреля 2011

Извините, что пишу свое решение на Python, я не знаю достаточно Java для написания на Java.

pat = re.compile('(?=(?:([A-Z])|[0-9])' ## This part verifies that
                 '[^ ]*'                ## there are at least one
                 '(?(1)\d|[A-Z]))'      ## letter and one digit.
                 '('   
                 '(?:(?<=[ ,])[A-Z0-9]|\A[A-Z0-9])'  # start of second group
                 '[A-Z0-9-/\\\\]*'
                 '[A-Z0-9](?= |\Z|,)'               # end of second group
                 ')',  
                 re.IGNORECASE) # this group 2 catches the string

.

Мое решение ловит нужную строку во второй группе: ((?:(?<={ ,])[A-Z0-9]|\A[A-Z0-9])[A-Z0-9-/\\\\]*[A-Z0-9](?= |\Z|,))

.

Часть, перед которой он проверяет, что в перехваченной строке присутствуют не менее одной буквы и не менее одной цифры:

(?(1)\d|[A-Z]) являетсяусловное регулярное выражение, которое означает «если группа (1) что-то перехватила, то здесь должна быть цифра, в противном случае должна быть буква»

Группа (1) имеет значение ([A-Z]) в (?=(?:([A-Z])|[0-9])

(?:([A-Z])|[0-9]) - это группа без захвата, которая соответствует букве (перехватывается) ИЛИ цифре, поэтому, когда она соответствует букве, группа (1) не пуста

.

Флаг re.IGNORECASE позволяет обрабатывать строки прописными или строчными буквами.

.

Во второй группе я обязан написать (?:(?<=[ ,])[A-Z0-9]|\A[A-Z0-9]), потому что утверждения с заданной длиной не фиксированной длины не допускаются.Эта часть обозначает один символ, который не может быть '-' , перед которым стоит пробел или заголовок строки.

С другой стороны, (?= |\Z[,) означает «конец строки илизапятая или пробел после '

.

Это регулярное выражение предполагает, что символы '-', '/', '\' не могут быть ни первым, ни последним символомзахваченной строки .Это правильно?

import re

pat = re.compile('(?=(?:([A-Z])|[0-9])' ## (from here)  This part verifies that
                 '[^ ]*'                 #              there are at least one
                 '(?(1)\d|[A-Z]))'      ## (to here)    letter and one digit.
                 '((?:(?<=[ ,])[A-Z0-9]|\A[A-Z0-9])'
                 '[A-Z0-9-/\\\\]*'
                 '[A-Z0-9](?= |\Z|,))',
                 re.IGNORECASE) # this group 2 catches the string

ch = "ALPHA13 10 ZZ 10-10 U-R open-office ,10B a10 UCS5000 -TR54 code vg4- DV-3000 SEA 300-BR  gt4/ui bn\\3K"

print [ mat.group(2) for mat in pat.finditer(ch) ]

s = "A35, 35A, B503X,1ABC5 " +\
     "AB-10, 10-AB, A10-BA, BA-A10, etc... " +\
     "10-10, open-office, etc."

print [ mat.group(2) for mat in pat.finditer(s) ]

результат

['ALPHA13', '10B', 'a10', 'UCS5000', 'DV-3000', '300-BR', 'gt4/ui', 'bn\\3K']
['A35', '35A', 'B503X', '1ABC5', 'AB-10', '10-AB', 'A10-BA', 'BA-A10']
1 голос
/ 20 апреля 2011

Просто используйте это:

([a-zA-Z]+[-\/\\]?[0-9]+|[0-9]+[-\/\\]?[a-zA-Z]+)

В Java \\ и \/ должны быть экранированы:

([a-zA-Z]+[-\\\/\\\\]?[0-9]+|[0-9]+[-\\\/\\\\]?[a-zA-Z]+)
0 голосов
/ 20 апреля 2011

Условие (A ИЛИ НЕ A) может быть опущено.Таким образом, символы могут быть проигнорированы.

for (String word : "10 10-10 open-office 10B A10 UCS5000 code DV-3000 300-BR".split (" "))
    if (word.matches ("(.*[A-Za-z].*[0-9])|(.*[0-9].*[A-Za-z].*)"))
         // do something

Вы не упомянули -x4, 4x-, 4-x-, -4-x или -4-x-, я ожидаю, что все они совпадут.

Мойвыражение ищет только что-то-альфа-что-то-цифры-что-то, где что-то может быть альфа, цифры или символы, и наоборот: что-то-альфа-что-то-цифры-что-то.Если что-то еще может произойти, например! # $ ~ () [] {} И т. Д., Оно станет длиннее.

Проверено с помощью scala:

scala> for (word <- "10 10-10 open-office 10B A10 UCS5000 code DV-3000 300-BR".split (" ")
     | if word.matches ("(.*[A-Za-z].*[0-9])|(.*[0-9].*[A-Za-z].*)")) yield word          
res89: Array[java.lang.String] = Array(10B, A10, UCS5000, DV-3000, 300-BR)

Слегка изменено для фильтрации совпадений:

String s = "A35, 35A, B53X, 1AC5, AB-10, 10-AB, A10-BA, BA-A10, etc. -4x, 4x- -4-x- 10-10, oe-oe, etc";
Pattern pattern  = java.util.regex.Pattern.compile ("\\b([^ ,]*[A-Za-z][^ ,]*[0-9])[^ ,]*|([^ ,]*[0-9][^ ,]*[A-Za-z][^ ,]*)\\b");
matcher = pattern.matcher (s);
while (matcher.find ()) { System.out.print (matcher.group () + "|") }

Но у меня все еще есть ошибка, которую я не нахожу:

A35|35A|B53X|1AC5|AB-10|10-AB|A10-BA|BA-A10|-4x|4x|-4-x|

4x должно быть 4x-, а -4-x должно быть -4-x-.

0 голосов
/ 20 апреля 2011

Мой первый проход дает

(^|\s)(?!\d+[-/\\]?\d+(\s|$))(?![A-Z]+[-/\\]?[A-Z]+(\s|$))([A-Z0-9]+[-/\\]?[A-Z0-9]+)(\s|$)

Извините, но он не отформатирован в java (вам нужно отредактировать \\ s и т. Д.).Кроме того, вы не можете использовать \b b / ca Граница слова - это все, что не является буквенно-цифровым и подчеркиванием, поэтому я использовал \s, а также начало и конец строки.

Это все еще немногоraw

EDIT

Версия 2, немного лучше, но может быть улучшена для производительности с помощью собственнических квантификаторов.Он соответствует ABC76 AB-32 3434-F и т. Д., Но не ABC или 19\23 и т. Д.

((?<=^)|(?<=\s))(?!\d+[-/\\]?\d+(\s|$))(?![A-Z]+[-/\\]?[A-Z]+(\s|$))([A-Z0-9]+[-/\\]?[A-Z0-9]+)((?=$)|(?=\s))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...