Поиск и замена регулярных выражений в больших файлах без получения OutOfMemoryException

Я создал небольшой код, который ищет строку Regex и заменяет ее чем-то другим, затем создает новый выходной файл с внесенными изменениями. Код, похоже, хорошо работает с меньшими файлами, но для файлов размером более 100 МБ я выдаю ошибку System.OutOfMemoryException.

Здесь мой код:

string foldername = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
    String.Format("FIXED_{0}.tmx",
        Path.GetFileNameWithoutExtension(textBox1.Text)));

string text = File.ReadAllText(textBox1.Text);
text = Regex.Replace(text, @"<seg\b[^>]*>", "<seg>", RegexOptions.Multiline);
text = Regex.Replace(text, @"<seg>
                    </tuv>", "<seg></seg></tuv>", RegexOptions.Multiline);

File.WriteAllText(foldername, text);

Visual studio выделяет раздел string text = File.ReadAllText(textBox1.Text);. Я думал, что, возможно, использование File.ReadAllLines будет работать лучше, но я не смог заставить его работать с регулярным выражением.

Может ли кто-нибудь помочь мне в этом? Я новичок на С# и, вероятно, мой код не самый лучший.

Ответ 1

Я боюсь, что вы должны выполнить замену самостоятельно. Ниже приведен пример кода, который использует state-machine для замены <seg\b[^>*]> на <seg>. Единственная проблема, с которой он столкнулся, заключается в том, что если файл заканчивается на <seg attr="", то на выход будет записываться только <seg.

enum TruncateSegState
{
    Idle,
    TagStart,
    TagStartS,
    TagStartSE,
    TagStartSEG,
    TagSEG
}

static void TruncateSeg(StreamReader input, StreamWriter output)
{
    TruncateSegState state = TruncateSegState.Idle;
    while (!input.EndOfStream)
    {
        char ch = (char)input.Read();
        switch (state)
        {
            case TruncateSegState.Idle:
                if (ch == '<')
                    state = TruncateSegState.TagStart;
                output.Write(ch);
                break;
            case TruncateSegState.TagStart:
                if (ch == 's')
                    state = TruncateSegState.TagStartS;
                else
                    state = TruncateSegState.Idle;
                output.Write(ch);
                break;
            case TruncateSegState.TagStartS:
                if (ch == 'e')
                    state = TruncateSegState.TagStartSE;
                else
                    state = TruncateSegState.Idle;
                output.Write(ch);
                break;
            case TruncateSegState.TagStartSE:
                if (ch == 'g')
                    state = TruncateSegState.TagStartSEG;
                else
                    state = TruncateSegState.Idle;
                output.Write(ch);
                break;
            case TruncateSegState.TagStartSEG:
                if (char.IsWhiteSpace(ch))
                    state = TruncateSegState.TagSEG;
                else
                {
                    state = TruncateSegState.Idle;
                    output.Write(ch);
                }
                break;
            case TruncateSegState.TagSEG:
                if (ch == '>')
                {
                    state = TruncateSegState.Idle;
                    output.Write(ch);
                }
                break;
        }
    }
}

Использование:

using (StreamReader reader = new StreamReader("input.txt"))
using (StreamWriter writer = new StreamWriter("temp.txt"))
    TruncateSeg(reader, writer);

После того, как вы сгенерировали temp.txt, вы используете его как вход для следующего метода, который добавляет отсутствующий тег </seg>.

enum ReplaceSegTuvState
{
    Idle,
    InsideSEG
}

static void ReplaceSegTuv(StreamReader input, StreamWriter output)
{
    ReplaceSegTuvState state = ReplaceSegTuvState.Idle;
    StringBuilder segBuffer = new StringBuilder();
    while (!input.EndOfStream)
    {
        char ch = (char)input.Read();
        switch (state)
        {
            case ReplaceSegTuvState.Idle:
                if (ch == '<')
                {
                    char[] buffer = new char[4];
                    int bufferActualLength = input.ReadBlock(buffer, 0, buffer.Length);
                    output.Write('<');
                    output.Write(buffer, 0, bufferActualLength);
                    if (bufferActualLength == buffer.Length && "seg>".SequenceEqual(buffer))
                    {
                        segBuffer.Clear();
                        state = ReplaceSegTuvState.InsideSEG;
                    }
                }
                else
                    output.Write(ch);
                break;
            case ReplaceSegTuvState.InsideSEG:
                if (ch == '<')
                {
                    char[] buffer = new char[5];
                    int bufferActualLength = input.ReadBlock(buffer, 0, buffer.Length);
                    if (bufferActualLength == buffer.Length && "/tuv>".SequenceEqual(buffer))
                    {
                        output.Write("</seg>");
                        output.Write("</tuv>");
                        state = ReplaceSegTuvState.Idle;
                    }
                    else
                    {
                        output.Write(segBuffer.ToString());
                        output.Write('<');
                        output.Write(buffer, 0, bufferActualLength);
                        state = ReplaceSegTuvState.Idle;
                    }
                }
                else if (!char.IsWhiteSpace(ch))
                {
                    output.Write(segBuffer.ToString());
                    output.Write(ch);
                    state = ReplaceSegTuvState.Idle;
                }
                else
                    segBuffer.Append(ch);
                break;
        }
    }
}

Использование:

using (StreamReader reader = new StreamReader("temp.txt"))
using (StreamWriter writer = new StreamWriter("output.txt"))
    ReplaceSegTuv(reader, writer);

Ответ 2

Попробуйте найти файлы, управляемые памятью; MSDN: http://msdn.microsoft.com/en-us/library/dd997372.aspx

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