title: smaller string gives bigger file size
body: <p>I am having a bit of a conundrum here... basically I am performing some very very basic file compression steps as follows:</p>
  
  <ol>
  <li>open file and read as string/into a string</li>
  <li>parse through the string, and replace repeating patterns with smaller size of text that represents the pattern (ex: aaaaaaaaaaa (11 chars) is replaced with [a#$%11] (8 chars))</li>
  <li>save the new, smaller string, into a separate file (can compare sizes)</li>
  </ol>
  
  <p>For some reason, even though the new string in memory is like, 3% smaller than the original string, when I save the string into a file, the file itself is BIGGER than the original file on the file system?? How is that even possible? If someone could explain that to me it would be great!</p>
  
  <p>Here is the code I am using to do this:</p>
  
  <pre><code>void bkg_DoWork(object sender, DoWorkEventArgs e)
      {
          try
          {
              string file = File.ReadAllText(this.txt_CompressFilename.Text);
  
              int olength = file.Length;
              int nlength = 0;
              decimal pct = 0;
  
              string lastchar = "";
              int count = 0;
  
              List&lt;RepeatingPattern&gt; SinglePatterns = new List&lt;RepeatingPattern&gt;();
              List&lt;RepeatingPattern&gt; DoublePatterns = new List&lt;RepeatingPattern&gt;();
              List&lt;RepeatingPattern&gt; TriplePatterns = new List&lt;RepeatingPattern&gt;();
              List&lt;RepeatingPattern&gt; QuadruplePatterns = new List&lt;RepeatingPattern&gt;();
  
              UpdateProgress("Read file contents", 0, 1, 6);
              UpdateProgress("Finding single character replacements.", pct, 1, 6);
  
              //single character replaces.
              for (int i = 0; i &lt; olength; i++)
              {
                  if (file[i].ToString() == lastchar)
                      count += 1;
                  else
                  {
                      //create a pattern, if the count is more than what a pattern's compressed pattern looks like to save space... 8 chars
                      //[a#$%#]
                      if (count &gt; 7)
                      {
                          //create and add a pattern to the list if necessary.
                          RepeatingPattern ptn = new RepeatingPattern(lastchar.ToString(), count);
                          if (!SinglePatterns.Contains(ptn))
                              SinglePatterns.Add(ptn);
                      }
                      count = 0;
                      lastchar = file[i].ToString();
                  }
              }
  
              //handle possible trailing pattern
              if (count &gt; 7)
              {
                  //create and add a pattern to the list if necessary.
                  RepeatingPattern ptn = new RepeatingPattern(lastchar.ToString(), count);
                  if (!SinglePatterns.Contains(ptn))
                      SinglePatterns.Add(ptn);
              }
  
              if (SinglePatterns.Count &gt; 0)
                  for (int i = 0; i &lt; SinglePatterns.Count; i++)
                      file = file.Replace(SinglePatterns[i].ToString(), SinglePatterns[i].ToReplaceString());
  
              nlength = file.Length;
              pct = (decimal)(((double)(olength - nlength) / olength) * 100);
  
              UpdateProgress("Found and replaced " + SinglePatterns.Count, pct, 2, 6);
              UpdateProgress("Finding double character replacements.", pct, 2, 6);
  
              lastchar = "";
              count = 0;
  
              //double character replaces.
              for (int i = 0; i + 1 &lt; file.Length; i = i + 2)
              {
                  if ("" + file[i] + "" + file[i + 1] == lastchar)
                      count += 1;
                  else
                  {
                      //create a pattern, if the count is more than what a pattern's compressed pattern looks like to save space... 8 chars
                      //[aa#$%#]
                      if (count &gt; 8)
                      {
                          //create and add a pattern to the list if necessary.
                          RepeatingPattern ptn = new RepeatingPattern(lastchar.ToString(), count);
                          if (!DoublePatterns.Contains(ptn))
                              DoublePatterns.Add(ptn);
                      }
                      count = 0;
                      lastchar = "" + file[i] + "" + file[i + 1];
                  }
              }
  
              //handle possible trailing pattern
              if (count &gt; 8)
              {
                  //create and add a pattern to the list if necessary.
                  RepeatingPattern ptn = new RepeatingPattern(lastchar.ToString(), count);
                  if (!DoublePatterns.Contains(ptn))
                      DoublePatterns.Add(ptn);
              }
  
              if (DoublePatterns.Count &gt; 0)
                  for (int i = 0; i &lt; DoublePatterns.Count; i++)
                      file = file.Replace(DoublePatterns[i].ToString(), DoublePatterns[i].ToReplaceString());
  
              nlength = file.Length;
              pct = (decimal)(((double)(olength - nlength) / olength) * 100);
  
              UpdateProgress("Found and replaced " + DoublePatterns.Count, pct, 3, 6);
              UpdateProgress("Finding triple character replacements.", pct, 3, 6);
  
              lastchar = "";
              count = 0;
  
              //triple character replaces.
              for (int i = 0; i + 2 &lt; file.Length; i = i + 3)
              {
                  if ("" + file[i] + "" + file[i + 1] + "" + file[i + 2] == lastchar)
                      count += 1;
                  else
                  {
                      //create a pattern, if the count is more than what a pattern's compressed pattern looks like to save space... 8 chars
                      //[aaa#$%#]
                      if (count &gt; 9)
                      {
                          //create and add a pattern to the list if necessary.
                          RepeatingPattern ptn = new RepeatingPattern(lastchar.ToString(), count);
                          if (!TriplePatterns.Contains(ptn))
                              TriplePatterns.Add(ptn);
                      }
                      count = 0;
                      lastchar = "" + file[i] + "" + file[i + 1] + "" + file[i + 2];
                  }
              }
  
              //handle possible trailing pattern
              if (count &gt; 9)
              {
                  //create and add a pattern to the list if necessary.
                  RepeatingPattern ptn = new RepeatingPattern(lastchar.ToString(), count);
                  if (!TriplePatterns.Contains(ptn))
                      TriplePatterns.Add(ptn);
              }
  
              if (TriplePatterns.Count &gt; 0)
                  for (int i = 0; i &lt; TriplePatterns.Count; i++)
                      file = file.Replace(TriplePatterns[i].ToString(), TriplePatterns[i].ToReplaceString());
  
              nlength = file.Length;
              pct = (decimal)(((double)(olength - nlength) / olength) * 100);
  
              UpdateProgress("Found and replaced " + TriplePatterns.Count, pct, 4, 6);
              UpdateProgress("Finding quadruple character replacements.", pct, 4, 6);
  
              lastchar = "";
              count = 0;
  
              //triple character replaces.
              for (int i = 0; i + 3 &lt; file.Length; i = i + 4)
              {
                  if ("" + file[i] + "" + file[i + 1] + "" + file[i + 2] + "" + file[i + 3] == lastchar)
                      count += 1;
                  else
                  {
                      //create a pattern, if the count is more than what a pattern's compressed pattern looks like to save space... 8 chars
                      //[aaaa#$%#]
                      if (count &gt; 10)
                      {
                          //create and add a pattern to the list if necessary.
                          RepeatingPattern ptn = new RepeatingPattern(lastchar.ToString(), count);
                          if (!QuadruplePatterns.Contains(ptn))
                              QuadruplePatterns.Add(ptn);
                      }
                      count = 0;
                      lastchar = "" + file[i] + "" + file[i + 1] + "" + file[i + 2] + "" + file[i + 3];
                  }
              }
  
              //Handle possible trailing pattern
              if (count &gt; 10)
              {
                  //create and add a pattern to the list if necessary.
                  RepeatingPattern ptn = new RepeatingPattern(lastchar.ToString(), count);
                  if (!QuadruplePatterns.Contains(ptn))
                      QuadruplePatterns.Add(ptn);
              }
  
              if (QuadruplePatterns.Count &gt; 0)
                  for (int i = 0; i &lt; QuadruplePatterns.Count; i++)
                      file = file.Replace(QuadruplePatterns[i].ToString(), QuadruplePatterns[i].ToReplaceString());
  
              nlength = file.Length;
              pct = (decimal)(((double)(olength - nlength) / olength) * 100);
  
              UpdateProgress("Found and replaced " + QuadruplePatterns.Count, pct, 5, 6);
              UpdateProgress("Saving new .cmp file...", pct, 5, 6);
  
              string newpath = this.txt_FolderName.Text + "\\" + Path.GetFileName(this.txt_CompressFilename.Text);
              newpath = newpath.Substring(0, newpath.LastIndexOf("."));
              newpath = newpath + ".cmp";
              File.WriteAllText(newpath, file);
  
              stopwatch.Stop();
  
              UpdateProgress("Compression completed! Time to compress file: " + string.Format("{0}", stopwatch.Elapsed), pct, 6, 6);
              string report = "Compression report\n\n";
  
              FileInfo inf = new FileInfo(this.txt_CompressFilename.Text);
              FileInfo infNew = new FileInfo(newpath);
              report += "Single character replacements made: " + SinglePatterns.Count + "\n\n";
              report += "Double character replacements made: " + DoublePatterns.Count + "\n\n";
              report += "Triple character replacements made: " + TriplePatterns.Count + "\n\n";
              report += "Quadruple character replacements made: " + QuadruplePatterns.Count + "\n\n";
              report += "Total compression ration achieved in string: " + pct + "% \n\n";
              report += "Old file size: " + inf.Length + "\nNew file size: " + infNew.Length + " in bytes.";
  
              report += "Total time to achieve compression: " + string.Format("{0}", stopwatch.Elapsed);
              e.Result = report;
          }
          catch (Exception ex)
          {
              e.Result = ex;
          }
      }
  </code></pre>
  
  <p>Here is the code for the RepeatingPattern class...</p>
  
  <pre><code>    using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;
  
  namespace Compressor
  {
      public class RepeatingPattern : IEquatable&lt;RepeatingPattern&gt;
      {
          public string RepeatingChar { get; set; }
          public int Count { get; set; }
  
          public RepeatingPattern()
          {
              this.RepeatingChar = "";
              this.Count = -1;
          }
  
          public RepeatingPattern(string rchar, int count)
          {
              this.RepeatingChar = rchar;
              this.Count = count;
          }
  
          public RepeatingPattern(string FromReplaceString)
          {
  
              FromReplaceString = FromReplaceString.Replace("[", "").Replace("]", "");
  
              List&lt;string&gt; parts = FromReplaceString.Split(new string[] { "#$%" }, StringSplitOptions.None).ToList();
  
              if (parts.Count != 2)
                  throw new ArgumentException("Invalid argument count. Must be in this format: [a#$%N]");
  
              try
              {
                  this.RepeatingChar = parts[0];
                  this.Count = int.Parse(parts[1]);
              }
              catch (Exception ex)
              {
                  throw new ArgumentException("Unable to cast the argument and create an object from it. Error: " + ex.Message);
              }
          }
          public override bool Equals(object obj)
          {
              RepeatingPattern tmp = obj as RepeatingPattern;
              if (tmp != null)
                  return base.Equals(tmp);
              else
                  throw new Exception("Invalid comparison type. Both objects must be of type RepeatingPattern");
          }
  
          public bool Equals(RepeatingPattern tmp)
          {
              return this.RepeatingChar == tmp.RepeatingChar &amp;&amp; this.Count == tmp.Count;
          }
  
          public override int GetHashCode()
          {
              return this.RepeatingChar.GetHashCode() ^ this.Count.GetHashCode();
          }
          public override string ToString()
          {
              string retval = "";
              for (int i = 0; i &lt; this.Count; i++)
                  retval += this.RepeatingChar;
  
              return retval;
          }
  
          public string ToReplaceString()
          {
              return "[" + this.RepeatingChar + "#$%" + this.Count + "]";
          }
      }
  }
  </code></pre>