Regex для розбиття рядка за допомогою пробілу, коли його не оточують одинарні або подвійні лапки


114

Я новачок у регулярних висловлюваннях і буду вдячний за вашу допомогу. Я намагаюся скласти вираз, який розділить приклад рядка, використовуючи всі пробіли, які не оточені одинарними або подвійними лапки. Моя остання спроба виглядає так: (?!")і не зовсім працює. Це розділення на простір перед цитатою.

Приклад введення:

This is a string that "will be" highlighted when your 'regular expression' matches something.

Бажаний вихід:

This
is
a
string
that
will be
highlighted
when
your
regular expression
matches
something.

Зауважте, що "will be"і 'regular expression'збережіть простір між словами.


Ви насправді використовуєте метод "розділити", чи буде достатньо циклічного циклу з методом "знайти" на Matcher?
erickson

9
"і тепер у нього дві проблеми"

Відповіді:


251

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

[^\s"']+|"([^"]*)"|'([^']*)'

Я додав групи захоплення, тому що ви не хочете, щоб цитати були у списку.

Цей код Java будує список, додаючи групу захоплення, якщо вона збігається з виключенням лапок, і додає загальний збіг регулярних виразів, якщо група захоплення не збігається (слово без котирування збігалося).

List<String> matchList = new ArrayList<String>();
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
Matcher regexMatcher = regex.matcher(subjectString);
while (regexMatcher.find()) {
    if (regexMatcher.group(1) != null) {
        // Add double-quoted string without the quotes
        matchList.add(regexMatcher.group(1));
    } else if (regexMatcher.group(2) != null) {
        // Add single-quoted string without the quotes
        matchList.add(regexMatcher.group(2));
    } else {
        // Add unquoted word
        matchList.add(regexMatcher.group());
    }
} 

Якщо ви не заперечуєте, щоб лапки були у поверненому списку, ви можете використовувати набагато простіший код:

List<String> matchList = new ArrayList<String>();
Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'");
Matcher regexMatcher = regex.matcher(subjectString);
while (regexMatcher.find()) {
    matchList.add(regexMatcher.group());
} 

1
Ян, дякую за вашу відповідь. До речі, я великий шанувальник EditPad.
carlsz

Що робити, якщо я хочу дозволити прострочені цитати в рядках \"?
Монсьє

3
Проблема з цією відповіддю полягає у неперевершеній цитаті: John's motherрезультати [John, s, mother]
розбиті

2
Щоб усунути проблему leonbloy контурів, ви можете змінити порядок операндів трохи і опускаєте цитати з пробільних-групи: "([^"]*)"|'([^']*)'|[^\s]+.
Привидник

1
Грунтуючись на цьому і інші відповіді, наступне регулярний вираз дозволяє уникнути символи всередині лапок: "([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+. Див stackoverflow.com/questions/5695240 / ...
озерний

15

У StackOverflow є кілька запитань, які висвітлюють це саме питання в різних контекстах, використовуючи регулярні вирази. Наприклад:

ОНОВЛЕННЯ : Зразок регулярного вираження для обробки одиночних і подвійних цитованих рядків. Ref: Як я можу розділити на рядок, крім випадків, коли всередині лапок?

m/('.*?'|".*?"|\S+)/g 

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

This
is
a
string
that
"will be"
highlighted
when
your
'regular expression'
matches
something.

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


Я думаю, що ваш регулярний вираз допускає невідповідні лапки, наприклад "буде" та "регулярні вирази".
Зак Скривена

@Zach - ти маєш рацію, але це ... оновив це, щоб виправити це на всякий випадок
Jay

6

Якщо ви хочете дозволити уникнуті лапки всередині рядка, ви можете використовувати щось подібне:

(?:(['"])(.*?)(?<!\\)(?>\\\\)*\1|([^\s]+))

Котировані рядки будуть групою 2, окремі без котирування слова - це група 3.

Ви можете спробувати його на різних рядках тут: http://www.fileformat.info/tool/regex.htm або http://gskinner.com/RegExr/


3

Регекс від Яна Гойвартса - це найкраще рішення, яке я знайшов поки що, але створює також порожні (нульові) збіги, які він виключає у своїй програмі. Ці порожні збіги також з’являються у тестерів регулярних виразів (наприклад, rubular.com). Якщо ви перегорнете пошукові запити (спочатку знайдіть цитовані частини, а не слова, розділені пробілом), ви можете зробити це за один раз із:

("[^"]*"|'[^']*'|[\S]+)+

2
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s

Це відповідатиме пробілам, не оточеним подвійними лапками. Мені потрібно використовувати min, max {0,99999}, тому що Java не підтримує * і + в перспективі.


1

Напевно, буде простіше шукати рядок, захоплюючи кожну частину та розділяючи її.

Зважаючи на це, ви можете розділити його на проміжки до і після "will be". Але я не можу придумати жодного способу вказати ігнорування простору між розділенням.

(не фактично Java)

string = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";

regex = "\"(\\\"|(?!\\\").)+\"|[^ ]+"; // search for a quoted or non-spaced group
final = new Array();

while (string.length > 0) {
    string = string.trim();
    if (Regex(regex).test(string)) {
        final.push(Regex(regex).match(string)[0]);
        string = string.replace(regex, ""); // progress to next "word"
    }
}

Також захоплення єдиних лапок може призвести до таких проблем:

"Foo's Bar 'n Grill"

//=>

"Foo"
"s Bar "
"n"
"Grill"

Ваше рішення не обробляє рядки з цитатами, які цитуються одночасно, які є прикладом Карла
Ян Гойвартс

1

String.split()Тут не корисно, оскільки немає способу розмежувати пробіли в лапках (не розділяти) і зовнішніх (розділити). Matcher.lookingAt()це, мабуть, те, що вам потрібно:

String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
str = str + " "; // add trailing space
int len = str.length();
Matcher m = Pattern.compile("((\"[^\"]+?\")|('[^']+?')|([^\\s]+?))\\s++").matcher(str);

for (int i = 0; i < len; i++)
{
    m.region(i, len);

    if (m.lookingAt())
    {
        String s = m.group(1);

        if ((s.startsWith("\"") && s.endsWith("\"")) ||
            (s.startsWith("'") && s.endsWith("'")))
        {
            s = s.substring(1, s.length() - 1);
        }

        System.out.println(i + ": \"" + s + "\"");
        i += (m.group(0).length() - 1);
    }
}

який дає такий вихід:

0: "This"
5: "is"
8: "a"
10: "string"
17: "that"
22: "will be"
32: "highlighted"
44: "when"
49: "your"
54: "regular expression"
75: "matches"
83: "something."

1

Мені сподобався підхід Маркуса, однак я змінив його, щоб я міг дозволити текст біля лапок і підтримувати як ", так і" цитати символів. Наприклад, мені потрібно було = "якесь значення", щоб не розділити його на [a =, " деяке значення "].

(?<!\\G\\S{0,99999}[\"'].{0,99999})\\s|(?<=\\G\\S{0,99999}\".{0,99999}\"\\S{0,99999})\\s|(?<=\\G\\S{0,99999}'.{0,99999}'\\S{0,99999})\\s"

1

Підхід Яна чудовий, але ось ще один для запису.

Якщо ви насправді хотіли розділити, як згадується в заголовку, зберігаючи лапки "will be"і 'regular expression', ви можете використовувати цей метод, який прямо не відповідає матчу (або замінити) шаблону, за винятком ситуацій s1, s2, s3 тощо

Регекс:

'[^']*'|\"[^\"]*\"|( )

Два лівих чергування збігаються 'quoted strings'і "double-quoted strings". Ми будемо ігнорувати ці матчі. Права сторона відповідає і фіксує пробіли до групи 1, і ми знаємо, що вони є правильними пробілами, оскільки вони не відповідали виразам зліва. Ми заміняємо їх на SplitHereпотім розділені SplitHere. Знову ж таки, це справжній розділений випадок, де ви хочете "will be", ні will be.

Ось повна робоча реалізація (дивіться результати на демонстрації в Інтернеті ).

import java.util.*;
import java.io.*;
import java.util.regex.*;
import java.util.List;

class Program {
public static void main (String[] args) throws java.lang.Exception  {

String subject = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
Pattern regex = Pattern.compile("\'[^']*'|\"[^\"]*\"|( )");
Matcher m = regex.matcher(subject);
StringBuffer b= new StringBuffer();
while (m.find()) {
    if(m.group(1) != null) m.appendReplacement(b, "SplitHere");
    else m.appendReplacement(b, m.group(0));
}
m.appendTail(b);
String replaced = b.toString();
String[] splits = replaced.split("SplitHere");
for (String split : splits) System.out.println(split);
} // end main
} // end Program

1

Якщо ви використовуєте c #, ви можете використовувати

string input= "This is a string that \"will be\" highlighted when your 'regular expression' matches <something random>";

List<string> list1 = 
                Regex.Matches(input, @"(?<match>\w+)|\""(?<match>[\w\s]*)""|'(?<match>[\w\s]*)'|<(?<match>[\w\s]*)>").Cast<Match>().Select(m => m.Groups["match"].Value).ToList();

foreach(var v in list1)
   Console.WriteLine(v);

Я спеціально додав " | <(? [\ W \ s] *)> ", щоб підкреслити, що ви можете вказати будь-які символи для групових фраз. (У цьому випадку я використовую <> для групування.

Вихід:

This
is
a
string
that
will be
highlighted
when
your
regular expression 
matches
something random

0

Я абсолютно впевнений, що це неможливо, використовуючи лише регулярні вирази. Перевірка того, що щось міститься в якомусь іншому тезі, є операцією аналізу. Це здається такою ж проблемою, як і спроба розбору XML за допомогою регулярного вираження - це неможливо зробити правильно. Можливо, ви зможете отримати бажаний результат, багаторазово застосовуючи не жадібний, неглобальний регулярний вираз, який відповідає цитуваним рядкам, тоді, як тільки ви нічого не зможете знайти, розділіть його на пробіли ..., що має ряд проблеми, включаючи відстеження початкового порядку всіх підрядків. Ваша найкраща ставка - просто написати дійсно просту функцію, яка переходить через рядок і витягує потрібні жетони.


Це можливо за допомогою регулярного вираження, дивіться деякі зразки, з якими я пов’язаний. Існує декілька варіантів цього питання, і я бачив кілька подібних запитань на SO, які вирішують це через регулярні вирази.
Джей

1
Знайте, коли не використовувати регулярний вираз - це корисніше знання, а потім мати можливість створити (?: (['"]) (. *?) (? <! \) (?> \\\) * \ 1 | ([ ^ \ s] +))
Рене

0

Пара сподіваємось корисних перетворень на прийняту відповідь Яна:

(['"])((?:\\\1|.)+?)\1|([^\s"']+)
  • Дозволяє уникнути цитат у рядках, що цитуються
  • Уникає повторення шаблону для одинарної та подвійної цитати; це також спрощує додавання більше символів цитування, якщо це потрібно (за рахунок ще однієї групи захоплення)

Це розбиває слова з апострофами, як-отyou're
Дизайн Адріана

0

Ви також можете спробувати це:

    String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something";
    String ss[] = str.split("\"|\'");
    for (int i = 0; i < ss.length; i++) {
        if ((i % 2) == 0) {//even
            String[] part1 = ss[i].split(" ");
            for (String pp1 : part1) {
                System.out.println("" + pp1);
            }
        } else {//odd
            System.out.println("" + ss[i]);
        }
    }

Вам слід дійсно додати пояснення, чому це має працювати - ви також можете додати код, а також коментарі до самого коду - у його теперішній формі він не дає жодних пояснень, які б допомогли решті громади зрозуміти, що ви зробили, щоб вирішити / відповісти на питання. Це особливо важливо для питань, на які вже є відповіді.
ishmaelMakitla

0

Далі повертає масив аргументів. Аргументи - це змінна "команда", розділена на пробіли, крім випадків, коли вони містяться в одинарних або подвійних лапках. Потім відповідність змінюється для видалення одинарних та подвійних лапок.

using System.Text.RegularExpressions;

var args = Regex.Matches(command, "[^\\s\"']+|\"([^\"]*)\"|'([^']*)'").Cast<Match>
().Select(iMatch => iMatch.Value.Replace("\"", "").Replace("'", "")).ToArray();

2
Чи можете ви додати трохи пояснень у свою відповідь, щоб інші могли легше її зрозуміти? В ідеалі ми хочемо уникати відповідей, що стосуються лише коду.
Jaquez

0

1-й одноклапник за допомогою String.split ()

String s = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
String[] split = s.split( "(?<!(\"|').{0,255}) | (?!.*\\1.*)" );

[This, is, a, string, that, "will be", highlighted, when, your, 'regular expression', matches, something.]

не розділяйте на порожній, якщо пробіл оточений одинарними або подвійними лапками,
розділеними на порожній, коли 255 символів зліва та всі символи праворуч від порожнього не є ні одиничними, ні подвійними лапками

адаптовано з оригінальної публікації (обробляє лише подвійні цитати)

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