Як зафіксувати кілька повторюваних груп?


87

Мені потрібно зафіксувати кілька груп одного і того ж шаблону. Припустимо, у мене є такий рядок:

HELLO,THERE,WORLD

І я написав наступний зразок

^(?:([A-Z]+),?)+$

Що я хочу, це захопити кожне окреме слово, щоб група 1 була: "ПРИВІТАЙ", група 2 - "ТАМ", а група 3 - "СВІТ" Те, що мій регулярний вираз фактично фіксує лише останнє, тобто " СВІТ ".

Я тестую свій регулярний вираз тут, і я хочу використовувати його із Swift (можливо, у Swift є спосіб якось отримати проміжні результати, щоб я міг їх використовувати?)

ОНОВЛЕННЯ: Я не хочу використовувати split. Мені просто зараз потрібно, як охопити всі групи, що відповідають шаблону, а не лише останню.


5
чому б не поділитися на ,?
rock321987

чому б не використати [A-Z]+або [^,]+не зафіксувати результати
rock321987

rock321987, я оновив вхідний рядок. Мені потрібно витягти саме той рядок, який відповідає наведеному вище зразку. І мені потрібно, щоб усі групи відповідали шаблону, а не лише остання. Я хочу знати, як це зробити за допомогою регулярного виразу.
phbelov

2
rock321987, що незрозуміло? Мені потрібно, щоб кожне слово рядка було узгодженою групою, але мій шаблон фіксує лише останнє ("СВІТ").
phbelov

1
використовуйте цю відповідь для пошуку всіх збігів
rock321987

Відповіді:


64

Маючи одну групу в шаблоні, ви можете отримати лише один точний результат у цій групі. Якщо ваша група захоплення повторюється за шаблоном (ви використовували файл+ квантор для сусідньої групи, яка не фіксує), зберігається лише останнє значення, яке відповідає їй.

Вам потрібно використовувати функції реалізації регулярного виразу вашої мови для знайти всі збіги шаблону, тоді вам доведеться видалити прив’язки та квантор групи, що не фіксує (а також можна опустити саму групу, яка не фіксує).

Крім того, розширте свій регулярний вираз і дозвольте шаблону містити одну групу захоплення на групу, яку ви хочете отримати в результаті:

^([A-Z]+),([A-Z]+),([A-Z]+)$

17
Як це буде скориговано з урахуванням різної кількості рядків? напр. Привіт, СВІТ і ПРИВІТ, ТАМ, МОЙ, СВІТ. Я шукаю лише один вираз для обробки обох прикладів та гнучкості, вбудованої для ще довших масивів рядків
Кріс,

12
@Chris Це не можна узагальнити. Як зазначено у відповіді, група захоплення може захоплювати лише одне, і немає можливості створити динамічну кількість груп захоплення.
Бармар,

7

Я думаю, вам потрібно щось подібне ....

b="HELLO,THERE,WORLD"
re.findall('[\w]+',b)

Який у Python3 повернеться

['HELLO', 'THERE', 'WORLD']

re.findall('\w+',b)на 2 символи коротше. Не потрібно класу символів, оскільки у вас є лише один вираз
Жан-Франсуа Фабр

3

Тільки надати додатковий приклад абзацу 2 у відповіді. Я не впевнений, наскільки критичним для вас є отримання трьох груп в одному матчі, а не трьох матчів за допомогою однієї групи. Наприклад, у паз:

def subject = "HELLO,THERE,WORLD"
def pat = "([A-Z]+)"
def m = (subject =~ pat)
m.eachWithIndex{ g,i ->
  println "Match #$i: ${g[1]}"
}

Match #0: HELLO
Match #1: THERE
Match #2: WORLD

3

Після прочитання відповіді командира байтів , я хочу внести невелике можливе вдосконалення:

Ви можете створити регулярний вираз, який буде відповідати будь-якому nслову, якщо ваше nпопереднє значення. Наприклад, якщо я хочу зіставити між 1 і 3 словами, регулярний вираз:

^([A-Z]+)(?:,([A-Z]+))?(?:,([A-Z]+))?$

буде відповідати наступним реченням, з однією, двома або трьома групами захоплення.

HELLO,LITTLE,WORLD
HELLO,WORLD
HELLO

Ви можете побачити повністю детальне пояснення цього регулярного виразу на Regex101 .

Як я вже говорив, генерувати цей регулярний вираз досить просто для будь-яких груп, які ви хочете використовувати улюблену мову. Оскільки я не дуже швидкий хлопець, ось приклад з рубіном:

def make_regexp(group_regexp, count: 3, delimiter: ",")
  regexp_str = "^(#{group_regexp})"
  (count - 1).times.each do
    regexp_str += "(?:#{delimiter}(#{group_regexp}))?"
  end
  regexp_str += "$"
  return regexp_str
end

puts make_regexp("[A-Z]+")

З огляду на це, я б радив не використовувати регулярні вирази в цьому випадку, існує багато інших чудових інструментів, від простих splitдо деяких моделей токенізації, залежно від ваших потреб. ІМХО, регулярний вираз не є одним із них. Наприклад, у ruby ​​я б використовував щось на зразок str.split(",")абоstr.scan(/[A-Z]+/)


2

Ключовою відмінністю є повторення захопленої групи замість того, щоб повторно взяти групу .

Як ви вже з'ясували, різниця полягає в тому, що повторення захопленої групи фіксує лише останню ітерацію. Захоплення повторної групи фіксує всі ітерації.

У PCRE (PHP):

((?:\w+)+),?
Match 1, Group 1.    0-5      HELLO
Match 2, Group 1.    6-11     THERE
Match 3, Group 1.    12-20    BRUTALLY
Match 4, Group 1.    21-26    CRUEL
Match 5, Group 1.    27-32    WORLD

Оскільки всі захоплення перебувають у групі 1, вам потрібна лише $1заміна.

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

((?:{{RE}})+)

Приклад у регулярному виразі101


1

Насправді у вас є одна група захоплення, яка буде збігатися кілька разів. Не кілька груп захоплення.

рішення javascript (js):

let string = "HI,THERE,TOM";
let myRegexp = /([A-Z]+),?/g;       //modify as you like
let match = myRegexp.exec(string);  //js function, output described below
while(match!=null){                 //loops through matches
    console.log(match[1]);          //do whatever you want with each match
    match = myRegexp.exec(bob);     //find next match
}

Вихід:

HI
THERE
TOM

Синтаксис:

// matched text: match[0]
// match start: match.index
// capturing group n: match[n]

Як бачите, це буде працювати на будь-яку кількість матчів.


0

Я знаю, що моя відповідь прийшла пізно, але це трапляється зі мною сьогодні, і я вирішив її таким підходом:

^(([A-Z]+),)+([A-Z]+)$

Отже, перша група (([A-Z]+),)+буде відповідати всім повтореним шаблонам, крім останньої, ([A-Z]+)яка відповідатиме фінальній. і це буде динамічно незалежно від того, скільки повторюваних груп у рядку.


3
Це не вирішення проблеми. Питання не в узгодженні рядка, а в захопленні всіх груп. Цей регулярний вираз все ще фіксує лише останній збіг для першої групи, що повторюється (з комою), а також збіг у кінцевій групі (без коми).
gdwarf

0

Вибачте, не Свіфт, просто доказ концепції найближчою мовою.

// JavaScript POC. Output:
// Matches:  ["GOODBYE","CRUEL","WORLD","IM","LEAVING","U","TODAY"]

let str = `GOODBYE,CRUEL,WORLD,IM,LEAVING,U,TODAY`
let matches = [];

function recurse(str, matches) {
    let regex = /^((,?([A-Z]+))+)$/gm
    let m
    while ((m = regex.exec(str)) !== null) {
        matches.unshift(m[3])
        return str.replace(m[2], '')
    }
    return "bzzt!"
}

while ((str = recurse(str, matches)) != "bzzt!") ;
console.log("Matches: ", JSON.stringify(matches))

Примітка: Якби ви дійсно збиралися використовувати це, ви б використовували позицію збігу, задану функцією збігу регулярних виразів, а не заміну рядка.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.