Удаление пустых строк - PullRequest
       10

Удаление пустых строк

1 голос
/ 24 марта 2009

Я написал следующее регулярное выражение и хотел бы автоматически удалять пустые строки, но не смог найти эквивалента RemoveEmptyEntries для Regex, который я нашел только для метода Split в строке.

string test = "{ key1 = { key2= xx } | key3 = y | key4 = z }";
string[] help = Regex.Split(test, "(=)|({)|(})|(\\|)");

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

Я буду очень и очень часто запускать этот код, поэтому мне он нужен как можно более эффективно. Обновления: так как это парсер, мне нужно сохранить токены, и я нашел только способ с помощью Regex сохранить их.

Ответы [ 7 ]

4 голосов
/ 24 марта 2009

Я не думаю, что эта опция встроена в RegEx. Но с C # 3.0 вы можете просто использовать .Where():

string[] help = Regex.Split(test, "(=)|({)|(})|(\\|)")
                     .Where(s => !string.IsNullOrEmpty(s)).ToArray();

Чтобы сделать это более эффективным, объявите RegEx один раз & mdash; возможно, на уровне класса или сделайте его статичным & mdash; вместо того, чтобы воссоздавать это все время. Кроме того, есть вероятность, что вы используете только возвращенный массив для перебора результатов. Вы можете сделать это быстрее, пропустив вызов .ToArray() в конце и просто оставив IEnumerable для вашей итерации.

//earlier
RegEx KeySplitter = new RegEx ("(=)|({)|(})|(\\|)");

.

//later
string test = ""; // 
for (string key in KeySplitter.Split(test).Where(s => !string.IsNullOrEmpty(s)))
{
    // ...
}

Одна из приятных сторон работы linq-to-objects заключается в том, что она будет повторяться только по вашим .Split результатам один раз , поскольку метод GetEnumerator в функции Where будет сделать ленивую оценку. В зависимости от того, что вам нужно сделать внутри цикла for, вы можете добиться аналогичной эффективности, добавив вызов .Select().

2 голосов
/ 24 марта 2009

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

the original regex:    (=)|({)|(})|(\|)
is equivalent to:      (=|{|}|\|)
is equivalent to:      ([={}|])

Все вышеприведенные выражения возвращают одинаковые 21 элемент, но они работают по-разному. Я настроил быстрый тест, пройдя 100 000 итераций операций Split() с использованием предварительно созданных объектов Regex с RegexOptions.Compiled и классом Stopwatch.

  • regex # 1 занимает 2002 мс на моем оборудовании
  • регулярное выражение # 2 занимает 1691мс
  • регулярное выражение № 3 занимает 1542 мс
  • регулярное выражение # 4 занимает 1839 мс (это ниже)

YMMV.

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

\s*([={}|])\s*

Возвращаемые элементы:

["", "{", "key1", "=", "", "{", "key2", "=", "xx", "}", "", "|", "key3", "=", "y", "|", "key4", "=", "z", "}", ""]

Несколько оставшихся пустых строк не должны создавать больших проблем с производительностью при итерации массива, и о них можно позаботиться (читай: игнорировать) при их обнаружении.

РЕДАКТИРОВАТЬ: Если вы измеряете производительность, возможно, вы найдете разделение на ([={}|]) и обрезка элементов массива "вручную" быстрее, чем разделение на \s*([={}|])\s*. Просто попробуйте, что лучше для вас.

1 голос
/ 24 марта 2009

Чтобы удалить пробелы из строки, просто сделайте это

Regex exp = new Regex(@"\s+");
string test = "{ key1 = { key2= xx } | key3 = y | key4 = z }";
string result = test.Replace(exp, string.Empty);

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

Regex.Replace(test, " ", string.Empty, RegexOptions.Compiled)

Вот что Джефф Этвуд (кстати, один из создателей StackOverFlow должен сказать о скомпилированном регулярном выражении )

После этого вы можете использовать свой код разделения для помещения ключей в массив строк.

1 голос
/ 24 марта 2009

Что касается эффективности: если звездам повезет, вы можете получить некоторую производительность, скомпилировав регулярное выражение:

Regex r = new Regex ("<regex goes here>", RegexOptions.Compiled);
0 голосов
/ 25 марта 2009

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

\s*[{}=|][\s{}=|]*

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

Добавление экранированных строк C # и объявление компиляции:

Regex regex = new Regex("\\s*[{}=|][\\s{}=|]*");
0 голосов
/ 25 марта 2009

Вместо разделения строки с помощью регулярного выражения вы можете изменить свое регулярное выражение и вернуть коллекцию совпадений. Примерно так:

string test = "{ key1 = { key2= xx } | key3 = y | key4 = z }";

Regex regex = new Regex("[={}|]|[^\\s={}|]{1,}");
MatchCollection matches = regex.Matches(test);

string[] help = new string[matches.Count];

for (int index = 0; index < matches.Count; index++)
{
    help[index] = matches[index].Value;                
}

Будет возвращено то же самое, что и ваше регулярное выражение минус пустые (пробелы) элементы в конечном массиве.

0 голосов
/ 24 марта 2009

Я не совсем понял ваш вопрос. Но «^ $» будет означать строку, которая заканчивается рядом с тем местом, где начинается, следовательно, пустую строку. Это помогает?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...