← Back to articles

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

Helped a guy on IRC with a method that reads a file or stream line by line, runs a custom replacement callback on each line, and writes the result out — without eating memory on large files. BSD-licensed, take it.

Tedd

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, [email protected]
// 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, [email protected]
 // 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
} 

Written by Tedd · Oslo, Norway

Updated 5 Jan 2017

Follow the journal via RSS