Regex для извлечения котируемых строк и символа запроса
У меня есть язык, который определяет строку как ограничиваемую одиночными или двойными кавычками, где разделитель спрятан внутри строки, удваивая его. Например, все следующие строки являются юридическими:
'This isn''t easy to parse.'
'Then John said, "Hello Tim!"'
"This isn't easy to parse."
"Then John said, ""Hello Tim!"""
У меня есть набор строк (определенный выше), ограниченный тем, что не содержит цитаты. То, что я пытаюсь сделать с помощью регулярных выражений, заключается в анализе каждой строки в списке. Например, вот вход:
"Некоторые строки # 1" ИЛИ "Некоторые строки # 2" И "Некоторые строки" # 3 "XOR
'Some" String "# 4' HOWDY" Some "" String "" # 5" FOO 'Some' 'String' '# 6'
Регулярное выражение для определения того, является ли строка такой формы тривиальным:
^(?:"(?:[^"]|"")*"|'(?:[^']|'')*')(?:\s+[^"'\s]+\s+(?:"(?:[^"]|"")*"|'(?:[^']|'')*')*
После выполнения вышеуказанного выражения, чтобы проверить, имеет ли он такую форму, мне нужно другое регулярное выражение, чтобы получить каждую строку с разделителем из ввода. Я планирую сделать это следующим образом:
Pattern pattern = Pattern.compile("What REGEX goes here?");
Matcher matcher = pattern.matcher(inputString);
int startIndex = 0;
while (matcher.find(startIndex))
{
String quote = matcher.group(1);
String quotedString = matcher.group(2);
...
startIndex = matcher.end();
}
Я хотел бы регулярное выражение, которое фиксирует символ кавычки в группе # 1, и текст внутри кавычек в группе # 2 (я использую Java Regex). Итак, для вышеприведенного ввода я ищу регулярное выражение, которое выдает следующий результат в каждой итерации цикла:
Loop 1: matcher.group(1) = "
matcher.group(2) = Some String #1
Loop 2: matcher.group(1) = '
matcher.group(2) = Some String #2
Loop 3: matcher.group(1) = "
matcher.group(2) = Some 'String' #3
Loop 4: matcher.group(1) = '
matcher.group(2) = Some "String" #4
Loop 5: matcher.group(1) = "
matcher.group(2) = Some ""String"" #5
Loop 6: matcher.group(1) = '
matcher.group(2) = Some ''String'' #6
Шаблоны, которые я пробовал до сих пор (un-escaped, а затем escape-код для Java-кода):
(["'])((?:[^\1]|\1\1)*)\1
"([\"'])((?:[^\\1]|\\1\\1)*)\\1"
(?<quot>")(?<val>(?:[^"]|"")*)"|(?<quot>')(?<val>(?:[^']|'')*)'
"(?<quot>\")(?<val>(?:[^\"]|\"\")*)\"|(?<quot>')(?<val>(?:[^']|'')*)'"
Оба они не работают при попытке скомпилировать шаблон.
Возможно ли такое регулярное выражение?
Ответы
Ответ 1
Сделайте класс утилиты, который соответствует вам:
class test {
private static Pattern pd = Pattern.compile("(\")((?:[^\"]|\"\")*)\"");
private static Pattern ps = Pattern.compile("(')((?:[^']|'')*)'");
public static Matcher match(String s) {
Matcher md = pd.matcher(s);
if (md.matches()) return md;
else return ps.matcher(s);
}
}
Ответ 2
Я не уверен, что это то, о чем вы просите, но вы можете просто написать код для синтаксического анализа строки и получить желаемые результаты (символ цитаты и внутренний текст) вместо обычного выражения.
class Parser {
public static ParseResult parse(String str)
throws ParseException {
if(str == null || (str.length() < 2)){
throw new ParseException();
}
Character delimiter = getDelimiter(str);
// Remove delimiters
str = str.substring(1, str.length() -1);
// Unescape escaped quotes in inner string
String escapedDelim = "" + delimiter + delimiter;
str = str.replaceAll(escapedDelim, "" + delimiter);
return new ParseResult(delimiter, str);
}
private static Character getDelimiter(String str)
throws ParseException {
Character firstChar = str.charAt(0);
Character lastChar = str.charAt(str.length() -1);
if(!firstChar.equals(lastChar)){
throw new ParseException(String.format(
"First char (%s) doesn't match last char (%s) for string %s",
firstChar, lastChar, str
));
}
return firstChar;
}
}
class ParseResult {
public final Character delimiter;
public final String contents;
public ParseResult(Character delimiter, String contents){
this.delimiter = delimiter;
this.contents = contents;
}
}
class ParseException extends Exception {
public ParseException(){
super();
}
public ParseException(String msg){
super(msg);
}
}
Ответ 3
Используйте это регулярное выражение:
"^('|\")(.*)\\1$"
Некоторые тестовые коды:
public static void main(String[] args) {
String[] tests = {
"'This isn''t easy to parse.'",
"'Then John said, \"Hello Tim!\"'",
"\"This isn't easy to parse.\"",
"\"Then John said, \"\"Hello Tim!\"\"\""};
Pattern pattern = Pattern.compile("^('|\")(.*)\\1$");
Arrays.stream(tests).map(pattern::matcher).filter(Matcher::find).forEach(m -> System.out.println("1=" + m.group(1) + ", 2=" + m.group(2)));
}
Вывод:
1=', 2=This isn''t easy to parse.
1=', 2=Then John said, "Hello Tim!"
1=", 2=This isn't easy to parse.
1=", 2=Then John said, ""Hello Tim!""
Если вам интересно, как захватить цитируемый текст в тексте:
Это регулярное выражение соответствует всем вариантам и фиксирует цитату в группе 1 и цитируемый текст в группе 6:
^((')|("))(.*?("\3|")(.*)\5)?.*\1$
Смотрите живая демонстрация.
Вот несколько тестовых кодов:
public static void main(String[] args) {
String[] tests = {
"'This isn''t easy to parse.'",
"'Then John said, \"Hello Tim!\"'",
"\"This isn't easy to parse.\"",
"\"Then John said, \"\"Hello Tim!\"\"\""};
Pattern pattern = Pattern.compile("^((')|(\"))(.*?(\"\\3|\")(.*)\\5)?.*\\1$");
Arrays.stream(tests).map(pattern::matcher).filter(Matcher::find)
.forEach(m -> System.out.println("quote=" + m.group(1) + ", quoted=" + m.group(6)));
}
Вывод:
quote=', quoted=null
quote=', quoted=Hello Tim!
quote=", quoted=null
quote=", quoted=Hello Tim!
Ответ 4
Использование регулярных выражений для этого типа проблемы очень сложно. Простой парсер, который не использует регулярное выражение, намного проще реализовать, понять и поддерживать.
Кроме того, такой простой синтаксический анализ может легко поддерживать такие вещи, как обратные слэш-экраны и преобразование последовательностей обратной косой черты в символы (например, "\n" преобразование в символ новой строки).
Ответ 5
Это можно сделать очень легко с помощью простого регулярного выражения, как показано ниже
private static Object[] checkPattern(String name, String regex) {
List<String> matchedString = new ArrayList<>();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(name);
while (matcher.find()) {
if (matcher.group().length() > 0) {
matchedString.add(matcher.group());
}
}
return matchedString.toArray();
}
@Test
public void quotedtextMultipleQuotedLines() {
String text = "He said, \"I am Tom\". She said, \"I am Lisa\".";
String quoteRegex = "(\"[^\"]+\")";
String[] strArray = {"\"I am Tom\"", "\"I am Lisa\""};
assertArrayEquals(strArray, checkPattern(text, quoteRegex));
}
Мы получаем строки как элементы массива здесь.