Freitag, 22. Oktober 2010

Replace Text in Streams

Today, I thought about replacing text in streams. I noticed that .NET 4 provides methods for type

String
to replace some text, but no methods for type
Stream
I found a way by converting the stream to a string in a first step and by replacing text in a second step.
new StreamReader(i).ReadToEnd().Replace("old text", "new text")

For a lot of cases this is alright and there is nothing to worry about. But if the source is a huge amount of data and I do not want to hold it in memory all the time, things get tricky. For these cases I wrote a simple method called

ReplaceTextInStream

The method gets four arguments: an input stream, an output stream, a string with a pattern to search for and a string with the replacement value. If one argument is null, a ArgumentNullException is thrown. Here is the code: Enjoy!

public static void ReplaceTextInStream(Stream input, Stream output, string pattern, string replacement) 
{
    if (input == null)
    {
        throw new ArgumentNullException("input");
    }
    if (output == null)
    {
        throw new ArgumentNullException("output");
    }
    if (pattern == null)
    {
        throw new ArgumentNullException("pattern");
    }
    if (replacement == null)
    {
        throw new ArgumentNullException("replacement");
    }
    StreamReader r = new StreamReader(input);
    StreamWriter w = new StreamWriter(output);
    // Input and Pattern are empty => Output = Replacement
    if(input.Length == 0 && pattern.Length == 0)
    {
        w.Write(replacement);
    }
    // Pattern is empty => Output = Input
    if (input.Length != 0 && pattern.Length == 0)
    {
        while (!r.EndOfStream)
        {
            w.Write((char)r.Read());
        }
    }
    // Init empty Stack
    List<char> stack = new List<char>();
    // Loop over input characters
    while (!r.EndOfStream)
    {
        // Read next input character
        char c = (char)r.Read();
        // Compare characters
        if (pattern[stack.Count] == c)
        {
            // Match => Put it on Stack
            stack.Add(c);
            // Match complete?
            if (stack.Count == pattern.Length)
            {
                // Write Replacement and clear Stack
                w.Write(replacement);
                stack.Clear();
            }
        }
        else
        {
            // No Macth => Stack filled?
            if (stack.Count != 0)
            {
                // Write and Clear Stack
                foreach (var tc in stack)
                {
                    w.Write(tc);
                }
                stack.Clear();
            }
            // Copy current character
            w.Write(c);
        }
    }
    // After Loop: Stack filled?
    if (stack.Count != 0)
    {
        // Write and Clear Stack
        foreach (var tc in stack)
        {
            w.Write(tc);
        }
        stack.Clear();
    }
    w.Flush();
}

Keine Kommentare: