Регулярное выражение для разделения на пробелы, если только в кавычках

Я хотел бы использовать метод .Net Regex.Split для разделения этой входной строки на массив. Он должен разбиваться на пробелы, если он не заключен в цитату.

Input: Вот "моя строка"   она имеет "шесть символов  " соответствует

Ожидаемый результат:

  • Здесь
  • является
  • моя строка
  • это
  • есть
  • шесть   матчи

Какой шаблон мне нужен? Также мне нужно указать любые RegexOptions?

Ответ 1

Никаких параметров не требуется

Regex:

\w+|"[\w\s]*"

С#:

Regex regex = new Regex(@"\w+|""[\w\s]*""");

Или, если вам нужно исключить символы:

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

Ответ 2

Решение Lieven получает большую часть пути, и, как он заявляет в своих комментариях, это просто вопрос о замене финала на решение Bartek. Конечным результатом является следующий рабочий regEx:

(?<=")\w[\w\s]*(?=")|\w+|"[\w\s]*"

Ввод: здесь "моя строка" имеет "шесть совпадений"

Вывод:

  • Здесь
  • является
  • "моя строка"
  • это
  • есть
  • "шесть матчей"

К сожалению, он включает в себя кавычки. Если вы используете следующее:

(("((?<token>.*?)(?<!\\)")|(?<token>[\w]+))(\s)*)

И явно зафиксируйте совпадение "токенов" следующим образом:

    RegexOptions options = RegexOptions.None;
    Regex regex = new Regex( @"((""((?<token>.*?)(?<!\\)"")|(?<token>[\w]+))(\s)*)", options );
    string input = @"   Here is ""my string"" it has   "" six  matches""   ";
    var result = (from Match m in regex.Matches( input ) 
                  where m.Groups[ "token" ].Success
                  select m.Groups[ "token" ].Value).ToList();

    for ( int i = 0; i < result.Count(); i++ )
    {
        Debug.WriteLine( string.Format( "Token[{0}]: '{1}'", i, result[ i ] ) );
    }

Отладочный вывод:

Token[0]: 'Here'
Token[1]: 'is'
Token[2]: 'my string'
Token[3]: 'it'
Token[4]: 'has'
Token[5]: ' six  matches'

Ответ 3

Я использовал Bartek Szabat ответ, но мне нужно было захватить больше, чем просто "\ w" персонажей в моих токенах. Чтобы решить эту проблему, я немного изменил его регулярное выражение, как и Grzenio:

Regular Expression: (?<match>[^\s"]+)|(?<match>"[^"]*")

C# String:          (?<match>[^\\s\"]+)|(?<match>\"[^\"]*\")

Код Bartek (который возвращает токены, лишенные закрывающих кавычек):

Regex
        .Matches(input, "(?<match>[^\\s\"]+)|(?<match>\"[^\"]*\")")
        .Cast<Match>()
        .Select(m => m.Groups["match"].Value)
        .ToList()
        .ForEach(s => Console.WriteLine(s));

Ответ 4

Главный ответ для меня не совсем подходит. Я пытался разделить эту строку на пробелы, но похоже, что она разбивается на точки ( "." ).

"the lib.lib" "another lib".lib

Я знаю, что вопрос задает вопрос о регулярных выражениях, но в итоге я написал функцию без регулярных выражений, чтобы сделать это:

    /// <summary>
    /// Splits the string passed in by the delimiters passed in.
    /// Quoted sections are not split, and all tokens have whitespace
    /// trimmed from the start and end.
    public static List<string> split(string stringToSplit, params char[] delimiters)
    {
        List<string> results = new List<string>();

        bool inQuote = false;
        StringBuilder currentToken = new StringBuilder();
        for (int index = 0; index < stringToSplit.Length; ++index)
        {
            char currentCharacter = stringToSplit[index];
            if (currentCharacter == '"')
            {
                // When we see a ", we need to decide whether we are
                // at the start or send of a quoted section...
                inQuote = !inQuote;
            }
            else if (delimiters.Contains(currentCharacter) && inQuote == false)
            {
                // We've come to the end of a token, so we find the token,
                // trim it and add it to the collection of results...
                string result = currentToken.ToString().Trim();
                if (result != "") results.Add(result);

                // We start a new token...
                currentToken = new StringBuilder();
            }
            else
            {
                // We've got a 'normal' character, so we add it to
                // the curent token...
                currentToken.Append(currentCharacter);
            }
        }

        // We've come to the end of the string, so we add the last token...
        string lastResult = currentToken.ToString().Trim();
        if (lastResult != "") results.Add(lastResult);

        return results;
    }

Ответ 5

Я нашел регулярное выражение в этом , чтобы быть весьма полезным. Чтобы заставить его работать на С#, вам нужно будет использовать класс MatchCollection.

//need to escape \s
string pattern = "[^\\s\"']+|\"([^\"]*)\"|'([^']*)'";

MatchCollection parsedStrings = Regex.Matches(line, pattern);

for (int i = 0; i < parsedStrings.Count; i++)
{
    //print parsed strings
    Console.Write(parsedStrings[i].Value + " ");
}
Console.WriteLine();

Ответ 6

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

"[^"]+"|\s?\w+?\s

Ответ 7

С небольшим количеством беспорядочности регулярные языки могут отслеживать четный/нечетный подсчет кавычек, но если ваши данные могут включать экранированные кавычки (\ "), то у вас возникают реальные проблемы с созданием или пониманием регулярного выражения, которое будет правильно отрегулируйте его.

Ответ 8

EDIT: Извините за мой предыдущий пост, это, очевидно, возможно.

Чтобы обрабатывать все символы, отличные от алфавитно-цифровых символов, вам нужно что-то вроде этого:

MatchCollection matchCollection = Regex.Matches(input, @"(?<match>[^""\s]+)|\""(?<match>[^""]*)""");
foreach (Match match in matchCollection)
        {
            yield return match.Groups["match"].Value;
        }

вы можете сделать foreach умнее, если вы используете .Net > 2.0

Ответ 9

Шон,

Я считаю, что следующее регулярное выражение должно это сделать

(?<=")\w[\w\s]*(?=")|\w+  

С уважением,
Ливны

Ответ 10

Взгляните на LSteinle " Разделить функцию, которая поддерживает классификаторы текста" в проекте кода

Вот фрагмент его проекта, который вас интересует.

using System.Text.RegularExpressions;

public string[] Split(string expression, string delimiter, string qualifier, bool ignoreCase)
{
    string _Statement = String.Format("{0}(?=(?:[^{1}]*{1}[^{1}]*{1})*(?![^{1}]*{1}))", 
                        Regex.Escape(delimiter), Regex.Escape(qualifier));

    RegexOptions _Options = RegexOptions.Compiled | RegexOptions.Multiline;
    if (ignoreCase) _Options = _Options | RegexOptions.IgnoreCase;

    Regex _Expression = New Regex(_Statement, _Options);
    return _Expression.Split(expression);
}

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

Ответ 11

Если вы хотите взглянуть на общее решение этой проблемы в виде бесплатного JavaScript-объекта с открытым исходным кодом, вы можете посетить http://splitterjsobj.sourceforge.net/ для живой демонстрации (и загрузки). Объект имеет следующие функции:

  • Пары пользовательских символов кавычек могут использоваться для выхода из разделителя (предотвратить разделение внутри кавычек). Кавычки могут быть экранированы с помощью пользовательского escape файла char и/или путем "сбрасывания двойной кавычки". Побег char может быть экранирован (сам по себе). В одном из 5 выходных массивов (свойств объекта) вывод не отображается. (Например, если escape char =/, "a///" b "не отображается как /" b)
  • Разделить на массив разделителей; проанализируйте файл за один вызов. (Выходные массивы будут вложенными.)
  • Все escape-последовательности, распознаваемые javascript, могут быть оценены в процессе разделения и/или в препроцессе.
  • Функциональность обратного вызова
  • Совместимость между браузерами

Объект также доступен как плагин jQuery, но как новый пользователь на этом сайте я могу включить только одну ссылку в это сообщение.