The Labs \ Source Viewer \ SSCLI \ System.Text.RegularExpressions \ RegexReplacement

  1. //------------------------------------------------------------------------------
  2. // <copyright file="RegexReplacement.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. // The RegexReplacement class represents a substitution string for
  16. // use when using regexs to search/replace, etc. It's logically
  17. // a sequence intermixed (1) constant strings and (2) group numbers.
  18. namespace System.Text.RegularExpressions
  19. {
  20.    
  21.     using System.Collections;
  22.    
  23.     internal sealed class RegexReplacement
  24.     {
  25. /*
  26.         * Since RegexReplacement shares the same parser as Regex,
  27.         * the constructor takes a RegexNode which is a concatenation
  28.         * of constant strings and backreferences.
  29.         */       
  30.         internal RegexReplacement(string rep, RegexNode concat, Hashtable _caps)
  31.         {
  32.             StringBuilder sb;
  33.             ArrayList strings;
  34.             ArrayList rules;
  35.             int slot;
  36.            
  37.             _rep = rep;
  38.            
  39.             if (concat.Type() != RegexNode.Concatenate)
  40.                 throw new ArgumentException(SR.GetString(SR.ReplacementError));
  41.            
  42.             sb = new StringBuilder();
  43.             strings = new ArrayList();
  44.             rules = new ArrayList();
  45.            
  46.             for (int i = 0; i < concat.ChildCount(); i++) {
  47.                 RegexNode child = concat.Child(i);
  48.                
  49.                 switch (child.Type()) {
  50.                     case RegexNode.Multi:
  51.                         sb.Append(child._str);
  52.                         break;
  53.                     case RegexNode.One:
  54.                         sb.Append(child._ch);
  55.                         break;
  56.                     case RegexNode.Ref:
  57.                         if (sb.Length > 0) {
  58.                             rules.Add(strings.Count);
  59.                             strings.Add(sb.ToString());
  60.                             sb.Length = 0;
  61.                         }
  62.                         slot = child._m;
  63.                        
  64.                         if (_caps != null && slot >= 0)
  65.                             slot = (int)_caps[slot];
  66.                        
  67.                         rules.Add(-Specials - 1 - slot);
  68.                         break;
  69.                     default:
  70.                         throw new ArgumentException(SR.GetString(SR.ReplacementError));
  71.                         break;
  72.                 }
  73.             }
  74.            
  75.             if (sb.Length > 0) {
  76.                 rules.Add(strings.Count);
  77.                 strings.Add(sb.ToString());
  78.             }
  79.            
  80.             _strings = strings;
  81.             _rules = rules;
  82.         }
  83.        
  84.         internal string _rep;
  85.         internal ArrayList _strings;
  86.         // table of string constants
  87.         internal ArrayList _rules;
  88.         // negative -> group #, positive -> string #
  89.         // constants for special insertion patterns
  90.        
  91.         internal const int Specials = 4;
  92.         internal const int LeftPortion = -1;
  93.         internal const int RightPortion = -2;
  94.         internal const int LastGroup = -3;
  95.         internal const int WholeString = -4;
  96.        
  97. /*
  98.        
  99.         * Given a Match, emits into the StringBuilder the evaluated
  100.         * substitution pattern.
  101.         */       
  102.         private void ReplacementImpl(StringBuilder sb, Match match)
  103.         {
  104.             for (int i = 0; i < _rules.Count; i++) {
  105.                 int r = (int)_rules[i];
  106.                 if (r >= 0)
  107.                     sb.Append((string)_strings[r]);
  108.                 else if (r < -Specials)
  109.                     sb.Append(match.GroupToStringImpl(-Specials - 1 - r));
  110.                 else {
  111.                     switch (-Specials - 1 - r) {
  112.                         case LeftPortion:
  113.                             sb.Append(match.GetLeftSubstring());
  114.                             break;
  115.                         case RightPortion:
  116.                             sb.Append(match.GetRightSubstring());
  117.                             break;
  118.                         case LastGroup:
  119.                             sb.Append(match.LastGroupToStringImpl());
  120.                             break;
  121.                         case WholeString:
  122.                             sb.Append(match.GetOriginalString());
  123.                             break;
  124.                     }
  125.                 }
  126.             }
  127.         }
  128.        
  129. /*
  130.         * The original pattern string
  131.         */       
  132.         internal string Pattern {
  133.             get { return _rep; }
  134.         }
  135.        
  136. /*
  137.         * Returns the replacement result for a single match
  138.         */       
  139.         internal string Replacement(Match match)
  140.         {
  141.             StringBuilder sb = new StringBuilder();
  142.            
  143.             ReplacementImpl(sb, match);
  144.            
  145.             return sb.ToString();
  146.         }
  147.        
  148. /*
  149.         * Three very similar algorithms appear below: replace (pattern),
  150.         * replace (evaluator), and split.
  151.         */       
  152.        
  153.        
  154. /*
  155.         * Replaces all ocurrances of the regex in the string with the
  156.         * replacement pattern.
  157.         *
  158.         * Note that the special case of no matches is handled on its own:
  159.         * with no matches, the input string is returned unchanged.
  160.         * The right-to-left case is split out because StringBuilder
  161.         * doesn't handle right-to-left string building directly very well.
  162.         */       
  163.         internal string Replace(Regex regex, string input, int count, int startat)
  164.         {
  165.             Match match;
  166.            
  167.             if (count < -1)
  168.                 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.CountTooSmall));
  169.             if (startat < 0 || startat > input.Length)
  170.                 throw new ArgumentOutOfRangeException("startat", SR.GetString(SR.BeginIndexNotNegative));
  171.            
  172.             if (count == 0)
  173.                 return input;
  174.            
  175.             match = regex.Match(input, startat);
  176.             if (!match.Success) {
  177.                 return input;
  178.             }
  179.             else {
  180.                 StringBuilder sb;
  181.                
  182.                 if (!regex.RightToLeft) {
  183.                     sb = new StringBuilder();
  184.                     int prevat = 0;
  185.                    
  186.                     do {
  187.                         if (match.Index != prevat)
  188.                             sb.Append(input, prevat, match.Index - prevat);
  189.                        
  190.                         prevat = match.Index + match.Length;
  191.                         ReplacementImpl(sb, match);
  192.                         if (--count == 0)
  193.                             break;
  194.                        
  195.                         match = match.NextMatch();
  196.                     }
  197.                     while (match.Success);
  198.                    
  199.                     if (prevat < input.Length)
  200.                         sb.Append(input, prevat, input.Length - prevat);
  201.                 }
  202.                 else {
  203.                     ArrayList al = new ArrayList();
  204.                     int prevat = input.Length;
  205.                    
  206.                     do {
  207.                         if (match.Index + match.Length != prevat)
  208.                             al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length));
  209.                        
  210.                         prevat = match.Index;
  211.                        
  212.                         for (int i = _rules.Count - 1; i >= 0; i--) {
  213.                             int r = (int)_rules[i];
  214.                             if (r >= 0)
  215.                                 al.Add((string)_strings[r]);
  216.                             else
  217.                                 al.Add(match.GroupToStringImpl(-Specials - 1 - r));
  218.                         }
  219.                        
  220.                         if (--count == 0)
  221.                             break;
  222.                        
  223.                         match = match.NextMatch();
  224.                     }
  225.                     while (match.Success);
  226.                    
  227.                     sb = new StringBuilder();
  228.                    
  229.                     if (prevat > 0)
  230.                         sb.Append(input, 0, prevat);
  231.                    
  232.                     for (int i = al.Count - 1; i >= 0; i--) {
  233.                         sb.Append((string)al[i]);
  234.                     }
  235.                 }
  236.                
  237.                 return sb.ToString();
  238.             }
  239.         }
  240.        
  241. /*
  242.         * Replaces all ocurrances of the regex in the string with the
  243.         * replacement evaluator.
  244.         *
  245.         * Note that the special case of no matches is handled on its own:
  246.         * with no matches, the input string is returned unchanged.
  247.         * The right-to-left case is split out because StringBuilder
  248.         * doesn't handle right-to-left string building directly very well.
  249.         */       
  250.         static internal string Replace(MatchEvaluator evaluator, Regex regex, string input, int count, int startat)
  251.         {
  252.             Match match;
  253.            
  254.             if (evaluator == null)
  255.                 throw new ArgumentNullException("evaluator");
  256.             if (count < -1)
  257.                 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.CountTooSmall));
  258.             if (startat < 0 || startat > input.Length)
  259.                 throw new ArgumentOutOfRangeException("startat", SR.GetString(SR.BeginIndexNotNegative));
  260.            
  261.             if (count == 0)
  262.                 return input;
  263.            
  264.             match = regex.Match(input, startat);
  265.            
  266.             if (!match.Success) {
  267.                 return input;
  268.             }
  269.             else {
  270.                 StringBuilder sb;
  271.                
  272.                 if (!regex.RightToLeft) {
  273.                     sb = new StringBuilder();
  274.                     int prevat = 0;
  275.                    
  276.                     do {
  277.                         if (match.Index != prevat)
  278.                             sb.Append(input, prevat, match.Index - prevat);
  279.                        
  280.                         prevat = match.Index + match.Length;
  281.                        
  282.                         sb.Append(evaluator(match));
  283.                        
  284.                         if (--count == 0)
  285.                             break;
  286.                        
  287.                         match = match.NextMatch();
  288.                     }
  289.                     while (match.Success);
  290.                    
  291.                     if (prevat < input.Length)
  292.                         sb.Append(input, prevat, input.Length - prevat);
  293.                 }
  294.                 else {
  295.                     ArrayList al = new ArrayList();
  296.                     int prevat = input.Length;
  297.                    
  298.                     do {
  299.                         if (match.Index + match.Length != prevat)
  300.                             al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length));
  301.                        
  302.                         prevat = match.Index;
  303.                        
  304.                         al.Add(evaluator(match));
  305.                        
  306.                         if (--count == 0)
  307.                             break;
  308.                        
  309.                         match = match.NextMatch();
  310.                     }
  311.                     while (match.Success);
  312.                    
  313.                     sb = new StringBuilder();
  314.                    
  315.                     if (prevat > 0)
  316.                         sb.Append(input, 0, prevat);
  317.                    
  318.                     for (int i = al.Count - 1; i >= 0; i--) {
  319.                         sb.Append((string)al[i]);
  320.                     }
  321.                 }
  322.                
  323.                 return sb.ToString();
  324.             }
  325.         }
  326.        
  327. /*
  328.         * Does a split. In the right-to-left case we reorder the
  329.         * array to be forwards.
  330.         */       
  331.         static internal string[] Split(Regex regex, string input, int count, int startat)
  332.         {
  333.             Match match;
  334.             string[] result;
  335.            
  336.             if (count < 0)
  337.                 throw new ArgumentOutOfRangeException("count", SR.GetString(SR.CountTooSmall));
  338.            
  339.             if (startat < 0 || startat > input.Length)
  340.                 throw new ArgumentOutOfRangeException("startat", SR.GetString(SR.BeginIndexNotNegative));
  341.            
  342.             if (count == 1) {
  343.                 result = new string[1];
  344.                 result[0] = input;
  345.                 return result;
  346.             }
  347.            
  348.             count -= 1;
  349.            
  350.             match = regex.Match(input, startat);
  351.            
  352.             if (!match.Success) {
  353.                 result = new string[1];
  354.                 result[0] = input;
  355.                 return result;
  356.             }
  357.             else {
  358.                 ArrayList al = new ArrayList();
  359.                
  360.                 if (!regex.RightToLeft) {
  361.                     int prevat = 0;
  362.                    
  363.                     for (;;) {
  364.                         al.Add(input.Substring(prevat, match.Index - prevat));
  365.                        
  366.                         prevat = match.Index + match.Length;
  367.                        
  368.                         // add all matched capture groups to the list.
  369.                         for (int i = 1; i < match.Groups.Count; i++) {
  370.                             if (match.IsMatched(i))
  371.                                 al.Add(match.Groups[i].ToString());
  372.                         }
  373.                        
  374.                         if (--count == 0)
  375.                             break;
  376.                        
  377.                         match = match.NextMatch();
  378.                        
  379.                         if (!match.Success)
  380.                             break;
  381.                     }
  382.                    
  383.                     al.Add(input.Substring(prevat, input.Length - prevat));
  384.                 }
  385.                 else {
  386.                     int prevat = input.Length;
  387.                    
  388.                     for (;;) {
  389.                         al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length));
  390.                        
  391.                         prevat = match.Index;
  392.                        
  393.                         // add all matched capture groups to the list.
  394.                         for (int i = 1; i < match.Groups.Count; i++) {
  395.                             if (match.IsMatched(i))
  396.                                 al.Add(match.Groups[i].ToString());
  397.                         }
  398.                        
  399.                         if (--count == 0)
  400.                             break;
  401.                        
  402.                         match = match.NextMatch();
  403.                        
  404.                         if (!match.Success)
  405.                             break;
  406.                     }
  407.                    
  408.                     al.Add(input.Substring(0, prevat));
  409.                    
  410.                     al.Reverse(0, al.Count);
  411.                 }
  412.                
  413.                 return (string[])al.ToArray(typeof(string));
  414.             }
  415.         }
  416.     }
  417.    
  418. }

Developer Fusion