C # Несколько Regex заменяет на строку - слишком много памяти - PullRequest
8 голосов
/ 16 апреля 2011

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

Обзор: Потоковая передача только текстового файла (иногда html) через ftp с добавлением StringBuilder для получения очень большой строки. Размер файла варьируется от 300 КБ до 30 МБ.

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

образец заменяет:

Regex re = new Regex("<A.*?>Table of Contents</A>", RegexOptions.IgnoreCase);
source = re.Replace(source, "");

При каждом запуске замены ракет памяти неба, я знаю, что это потому, что строка неизменна в C #, и ей нужно сделать копию - даже если я вызываю GC.Collect(), это все равно недостаточно для файла 30 МБ .

Какой-нибудь совет относительно лучшего подхода или способа выполнения множественных замен регулярных выражений с использованием постоянной памяти (сделайте 2 копии (т.е. 60 МБ в памяти), выполните поиск, отбросьте копию обратно до 30 МБ)?

Обновление:

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

  1. Если возможно, разбить строку на куски, см. Ответ manojlds, чтобы найти путь к нему при чтении файла - поиск подходящих конечных точек.

  2. Если вы не можете разделить потоки, по крайней мере разделите их позже, если это возможно - см. Ответ ChrisWue о некоторых внешних инструментах, которые могут помочь в этом процессе для передачи файлов.

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

  4. Объедините регулярное выражение, когда это возможно, это сокращает количество замен, когда регулярные выражения не основаны друг на друге (полезно в этом случае для очистки неверного ввода) - см. Ответ Брайана Райхла для примера кода.

Спасибо всем!

Ответы [ 4 ]

2 голосов
/ 16 апреля 2011

У меня довольно похожая ситуация.

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

Source = Regex.Replace(source, pattern, replace, RegexOptions.Compiled);

В зависимости от вашей ситуации это может существенно повлиять на скорость.

Не полное решение, особенно дляфайлы размером более 3-4 Мб.

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

Вместо использования:

<a.*?>xxx

используйте

<a[^<>]*>xxx

Причина в том, что несмешный операторвынуждает механизм регулярных выражений проверять каждый символ по сравнению с остальной частью выражения, тогда как [^ <>] требует только сравнения текущего символа с <и> и останавливается, как только условие выполнено.В больших файлах это может иметь значение между половиной секунды и остановкой приложения.

Это не полностью решает проблему, но должно помочь.

2 голосов
/ 16 апреля 2011

В зависимости от природы RegEx, вы можете объединить их в одно регулярное выражение и использовать перегрузку Replace (), которая принимает делегат MatchEvaluator для определения замены из совпадающей строки.

Regex re = new Regex("First Pattern|Second Pattern|Super(Mega)*Delux", RegexOptions.IgnoreCase);

source = re.Replace(source, delegate(Match m)
{
    string value = m.Value;

    if(value.Equals("first pattern", StringComparison.OrdinalIgnoreCase)
    {
        return "1st";
    }
    else if(value.Equals("second pattern", StringComparison.OrdinalIgnoreCase)
    {
        return "2nd";
    }
    else
    {
        return "";
    }
});

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

2 голосов
/ 16 апреля 2011

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

http://www.developer.com/design/article.php/3719741/Building-a-Regular-Expression-Stream-Search-with-the-NET-Framework.htm

1 голос
/ 16 апреля 2011

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

Одна проблема с большой строкой состоит в том, что объекты размером более 85 000 байт считаются большими объектами и помещаются в кучу больших объектов, которая не сжимается, что может привести к неожиданным ситуациям нехватки памяти.

Другой вариант - передать его через внешний инструмент, такой как sed или awk.

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