2009-03-24 6 views
1

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

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

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

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

+0

Извините, но что вы имеете в виду «чтобы получить пустые строки удаляются автоматически»? Вы хотите удалить пробелы? Это не очень понятно (по крайней мере для меня) – Alekc

+0

Мне тоже не ясно. Можете ли вы опубликовать несколько строк до и после строк, показывающих, какие у вас есть данные и какой результат вы ожидаете? –

+0

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

ответ

2

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

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

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

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

YMMV.

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

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

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

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

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

EDIT: Если вы измеряете производительность, возможно, что вы находите разделение на ([={}|]) и обрезка элементов массива «вручную» происходит быстрее, чем расщепление на \s*([={}|])\s*. Просто попробуйте то, что лучше для вас.

+0

обещал завершить uptote –

+0

В качестве дополнительного примечания: учитывая, что после раскола осталось 4 пустых строки, мой .Where() код, вероятно, лучший способ их отфильтровать. –

+0

Я получаю 21 элемент также с первым регулярным выражением, но я получаю 4 мс на 1000 итераций при измерении производительности с помощью StopWatch. Я не понял твое последнее предложение, хотя, может быть, ты дашь мне точный код - я получаю неудачную последовательность побега. – weismat

0

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

+0

Я думаю, что он ищет эквивалент регулярного выражения hte, эквивалентный StringSplitOptions.RemoveEmptyEntries. –

+0

Это то, что я пытался сказать в своей первой строке - извините, что я не являюсь носителем английского языка. – weismat

+0

Нет проблем, я тоже. –

4

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

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

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

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

.

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

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

+0

ОК - это, но это означает, что при выполнении синтаксического анализа я добавляю дополнительный запрос LINQ. Не очень приятно - но, по крайней мере, я не пропустил что-то тривиальное из документации. – weismat

+0

Не «(=) | ({) | (}) | (\ |)« очень сложный способ сказать «[= {} |]» в первую очередь? Или я пропустил что-то принципиальное здесь? Выражение класса символов должно быть намного быстрее. – Tomalak

+0

И * if * выражение класса символа - это то, что происходит после OP, тогда существует String.Split (Char []), чтобы избежать регулярного выражения. – Tomalak

1

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

Regex r = new Regex ("<regex goes here>", RegexOptions.Compiled); 
1

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

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 имеет значение say about compiled regex)

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

+0

К OP упомянул, что это то, что будет происходить много раз подряд, поэтому скомпилированный является правильным выбором. –

+0

Лучше показать дверь, чем переносить их через дверь! –

+0

Я сменил его на скомпилированный, но моя проблема - это пустые элементы строки в результате, а не удаление пробелов до ... – weismat

0

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

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

Спасибо за комментарий. Я также попробую эту возможность, сравнивая мой парсер с пустой строкой или без нее. – weismat

0

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

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

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

Добавление C# строковые побег и сборник декларации:

Regex regex = new Regex("\\s*[{}=|][\\s{}=|]*");