C#: Replacement of $REPLACE_ID$ tokens (file or stream input+output)

By | 2010.03.12

Helped out a guy on IRC making a method that replaces text in a file/stream, thought I’d share it. Basically it reads from one file line by line and writes to another while executing a custom replacement method on every line.

Note: This code is not optimized for speed. Although it will perform very well, it CAN be optimized a lot. This is meant as coder friendly code. However it will not use more memory when processing large files.

// Needs:
//using System;
//using System.IO;
//using System.Collections.Generic;
//using System.Text.RegularExpressions;

// By Tedd Hansen, 2010.03.12, tedd@konge.net
// BSD-licensed, no warranties given

private void Test()
{
    // First of all we need to instansiate a copy of ReplacementClass we can use
    ReplacementClass myReplacementClass = new ReplacementClass();
    // Build our replacement map
    myReplacementClass.ReplacementMap.Clear();
    myReplacementClass.ReplacementMap.Add("Color1", "#FFFFFF");
    myReplacementClass.ReplacementMap.Add("Color2", "#FF00FF");

    // Now do replacement
    // Note that we provide a "ReplacementMethod" which will do our replacement.
    myReplacementClass.ReplaceFile(@"C:\Temp\SourceFile.txt", @"C:\Temp\DestinationFile.txt", new MatchEvaluator(myReplacementClass.ReplacementMethod));

}

/// <summary>
/// Contains Replace methods for replacing $REPLACE_ID$ tokens in file or stream input+output
/// </summary>
public class ReplacementClass
{
    // By Tedd Hansen, 2010.03.12, tedd@konge.net
    // BSD-licensed, no warranties given
    #region ReplacementMethod(): Sample replacement method that looks up ID's in a dictionary.
    /// <summary>
                /// Contains a map of ID (from $REPLACE_ID$ found in source file) to replacement text.
                /// Note that ID is case insensitive.
                /// </summary>
    public Dictionary<string, string> ReplacementMap = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);

    /// <summary>
                /// This is where the replacement happens. Anything you return here will replace $REPLACE_*$ in the line.
                /// </summary>
    public string ReplacementMethod(Match m)
    {
        // Get the ID part of string $REPLACE_ID$
        string replacementID = m.Groups[1].Value;

        // Look for this ID in our replacement dictionary
        if (ReplacementMap.ContainsKey(replacementID))
            return ReplacementMap[replacementID];

        // Here is an example of hardcoded replacements... they may make more sense than the above.
        switch (replacementID.ToLowerInvariant())
        {
            case "time":
                return DateTime.Now.ToString("HH:mm:ss");
                break;
            case "date":
                return DateTime.Now.ToString("yyyy-MM-dd");
                break;
        }

        // Default: Replace it with nothing
        return "";
    }
    #endregion

    // Regex object used to quickly and easily locate text to replace
    // This is where we actually define what to look for
    private Regex ReplacementFinderRegex = new Regex(@"\$REPLACE_([^\$]+)\$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);

    #region ReplaceFile(): File system wrapper method for ReplaceStream()
    /// <summary>
                /// Copy a file and replace content while doing so.
                /// </summary>
                /// <param name="sourceFile">Source file</param>
                /// <param name="destinationFile">Desintation file</param>
                /// <param name="replacementFunctionDelefate"></param>
    public void ReplaceFile(string sourceFile, string destinationFile, MatchEvaluator replacementMethodDelegate)
    {
        // By using "using" we ensure that files are closed properly regardless what we forget to do or or error occurs

        // Open input file so we get a FileStream object
        using (FileStream inStream = File.Open(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            // Open output file so we get a FileStream object
            using (FileStream outStream = File.Open(destinationFile, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                // We use the ReplaceStream function to do the actual copying and replacement
                ReplaceStream(inStream, outStream, replacementMethodDelegate);
            }
        }
    }
    #endregion

    #region ReplaceStream(): Line by line stream copy with replacement
    /// <summary>
                /// Read from input stream and write to output stream while modifying the content.
                /// Note: This method can be used on streams coming directly from SQL and streams going directly to webpage (Response.Stream or something)
                /// </summary>
                /// <param name="inStream">Input stream</param>
                /// <param name="outStream">Output stream</param>
    public void ReplaceStream(FileStream inStream, FileStream outStream, MatchEvaluator replacementMethodDelegate)
    {
        using (StreamReader inStreamReader = new StreamReader(inStream))
        {
            using (StreamWriter outStreamWriter = new StreamWriter(outStream))
            {
                string inLine = null;
                while ((inLine = inStreamReader.ReadLine()) != null)
                {
                    // Send the line through our replacement method using Regex
                    string outLine = null;
                    lock (ReplacementFinderRegex)
                    {
                        outLine = ReplacementFinderRegex.Replace(inLine, replacementMethodDelegate);
                    }

                    // Write the line to output stream
                    if (outLine != null)
                        outStreamWriter.WriteLine(outLine);
                }
            }
        }
    }
    #endregion
}

 

Leave a Reply