Могут ли регулярные выражения достичь этого? - PullRequest
3 голосов
/ 10 февраля 2010

Я пытаюсь разбить строку на токены (с помощью регулярных выражений) следующим образом:

Пример # 1
входная строка: 'hello'
Первый токен: '
второй токен: hello
третий токен: '

Пример # 2
входная строка: 'hello world'
Первый токен: '
второй токен: hello world
третий токен: '

Пример № 3
входная строка: hello world
Первый токен: hello
второй токен: world

Т.е., разделяйте строку только в том случае, если она НЕ в одинарных кавычках, а одинарные кавычки должны быть в их собственном токене.

Это то, что я имею до сих пор:

string pattern = @"'|\s";
Regex RE = new Regex(pattern);
string[] tokens = RE.Split("'hello world'");

Это будет работать, например, № 1 и пример № 3, но не будет работать, например, № 2. Мне интересно, есть ли теоретически способ достичь того, что я хочу, с помощью регулярных выражений

Ответы [ 8 ]

5 голосов
/ 10 февраля 2010

Вы можете создать простой лексер, который будет включать в себя потребление каждого из токенов один за другим. Таким образом, у вас будет список регулярных выражений, и вы будете пытаться сопоставить одно из них в каждой точке. Это самый простой и понятный способ сделать это, если ваш вклад выходит за рамки очень простого.

3 голосов
/ 10 февраля 2010

Используйте парсер токена для разделения на токены. Используйте регулярные выражения, чтобы найти строковые шаблоны

2 голосов
/ 10 февраля 2010

'[^']+' будет соответствовать текст внутри одинарных кавычек. Если вы хотите, чтобы это сгруппировалось, (')([^']+)('). Если совпадений не найдено, просто используйте обычное разбиение строки. Я не думаю, что имеет смысл пытаться сделать все это в одном регулярном выражении.

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

1 голос
/ 10 февраля 2010

Вам будет трудно использовать Split здесь, но вы можете использовать MatchCollection, чтобы найти все совпадения в вашей строке:

string str = "hello world, 'HELLO WORLD': we'll be fine.";
MatchCollection matches = Regex.Matches(str, @"(')([^']+)(')|(\w+)");

Регулярное выражение ищет строку между одинарными кавычками. Если он не может его найти, ему нужно всего одно слово.
Теперь это становится немного сложнее - .net возвращает коллекцию Match s. Каждое совпадение имеет несколько Group с - у первой группы есть вся строка ('hello world'), но у остальных есть под-совпадения (', hello world, '). Кроме того, вы получаете много пустых неудачных групп.
Вы все еще можете легко повторять и получать свои совпадения. Вот пример использования LINQ:

var tokens = from match in matches.Cast<Match>()
             from g in match.Groups.Cast<Group>().Skip(1)
             where g.Success
             select g.Value;

tokens теперь коллекция строк:
hello, world, ', HELLO WORLD, ', we, ll, be, fine

1 голос
/ 10 февраля 2010

Хотя было бы возможно сопоставить ' и текст внутри по отдельности, а также альтернативно сопоставить только текст, RegExp не допускает неопределенного количества совпадений. Или, лучше сказать, вы можете сопоставить только те объекты, которые вы явно указали в выражении. Таким образом, ((\w+)+\b) теоретически может соответствовать всем словам одно за другим. Внешняя группа будет правильно соответствовать всему тексту, а также внутренняя группа будет корректно соответствовать словам по отдельности, но вы сможете ссылаться только на последнее совпадение.

Нет способа сопоставить группу совпадающих совпадений (странное предложение). Единственный возможный способ - сопоставить строку и , а затем разделить ее на отдельные слова.

1 голос
/ 10 февраля 2010

Не совсем то, что вы пытаетесь сделать, но условия регулярного выражения могут помочь при поиске решения:

(?<quot>')?(?<words>(?(quot)[^']|\w)+)(?(quot)')

Если цитата найдена, она совпадает до тех пор, пока не будет найдена не цитата. Иначе смотрит на символы слов. Ваши результаты в группах "quot" и "words".

1 голос
/ 10 февраля 2010

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

foreach (String s in Regex.Split(input, @"('[^']+')")) {
    // Check first if s is a quote.
    // If so, split out the quotes.
    // If not, do what you intend to do.
}

(Примечание: вам нужно использовать скобки в шаблоне, чтобы Regex.Split их тоже возвращал)

0 голосов
/ 10 февраля 2010

Попробуйте это регулярное выражение:

([']*)([a-z]+)([']*)

Находит 1 или более одинарных кавычек в начале и конце строки. Затем он находит 1 или более символов в наборе a-z (если вы не установите его без учета регистра, он найдет только символы нижнего регистра). Он группирует их так, что в группе 1 есть ', в группе 2 (или более) есть слова, разделенные на все, что не является символом a - z, и последняя группа имеет одинарную кавычку, если она существует.

...