replace.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. //
  2. // assembly: System
  3. // namespace: System.Text.RegularExpressions
  4. // file: replace.cs
  5. //
  6. // author: Dan Lewis ([email protected])
  7. // (c) 2002
  8. //
  9. // Permission is hereby granted, free of charge, to any person obtaining
  10. // a copy of this software and associated documentation files (the
  11. // "Software"), to deal in the Software without restriction, including
  12. // without limitation the rights to use, copy, modify, merge, publish,
  13. // distribute, sublicense, and/or sell copies of the Software, and to
  14. // permit persons to whom the Software is furnished to do so, subject to
  15. // the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be
  18. // included in all copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  21. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  22. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  23. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  24. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  25. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  26. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  27. //
  28. using System;
  29. using System.Text;
  30. using System.Collections;
  31. using Parser = System.Text.RegularExpressions.Syntax.Parser;
  32. namespace System.Text.RegularExpressions {
  33. class ReplacementEvaluator {
  34. public static string Evaluate (string replacement, Match match) {
  35. ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);
  36. return ev.Evaluate (match);
  37. }
  38. public ReplacementEvaluator (Regex regex, string replacement) {
  39. this.regex = regex;
  40. terms = new ArrayList ();
  41. Compile (replacement);
  42. }
  43. public string Evaluate (Match match) {
  44. StringBuilder result = new StringBuilder ();
  45. foreach (Term term in terms)
  46. result.Append (term.GetResult (match));
  47. return result.ToString ();
  48. }
  49. // private
  50. private void Compile (string replacement) {
  51. replacement = Parser.Unescape (replacement);
  52. StringBuilder literal = new StringBuilder ();
  53. int ptr = 0;
  54. char c;
  55. Term term = null;
  56. while (ptr < replacement.Length) {
  57. c = replacement[ptr ++];
  58. if (c == '$') {
  59. if (replacement[ptr] != '$')
  60. term = CompileTerm (replacement, ref ptr);
  61. else
  62. ++ ptr;
  63. }
  64. if (term != null) {
  65. term.Literal = literal.ToString ();
  66. terms.Add (term);
  67. term = null;
  68. literal.Length = 0;
  69. }
  70. else
  71. literal.Append (c);
  72. }
  73. if (term == null && literal.Length > 0) {
  74. terms.Add (new Term (literal.ToString ()));
  75. }
  76. }
  77. private Term CompileTerm (string str, ref int ptr) {
  78. char c = str[ptr];
  79. if (Char.IsDigit (c)) { // numbered group
  80. int n = Parser.ParseDecimal (str, ref ptr);
  81. if (n < 0 || n > regex.GroupCount)
  82. throw new ArgumentException ("Bad group number.");
  83. return new Term (TermOp.Match, n);
  84. }
  85. ++ ptr;
  86. switch (c) {
  87. case '{': { // named group
  88. string name = Parser.ParseName (str, ref ptr);
  89. if (str[ptr ++] != '}' || name == null)
  90. throw new ArgumentException ("Bad group name.");
  91. int n = regex.GroupNumberFromName (name);
  92. if (n < 0)
  93. throw new ArgumentException ("Bad group name.");
  94. return new Term (TermOp.Match, n);
  95. }
  96. case '&': // entire match
  97. return new Term (TermOp.Match, 0);
  98. case '`': // text before match
  99. return new Term (TermOp.PreMatch, 0);
  100. case '\'': // text after match
  101. return new Term (TermOp.PostMatch, 0);
  102. case '+': // last group
  103. return new Term (TermOp.Match, regex.GroupCount - 1);
  104. case '_': // entire text
  105. return new Term (TermOp.All, 0);
  106. default:
  107. throw new ArgumentException ("Bad replacement pattern.");
  108. }
  109. }
  110. private Regex regex;
  111. private ArrayList terms;
  112. private enum TermOp {
  113. None, // no action
  114. Match, // input within group
  115. PreMatch, // input before group
  116. PostMatch, // input after group
  117. All // entire input
  118. }
  119. private class Term {
  120. public Term (TermOp op, int arg) {
  121. this.op = op;
  122. this.arg = arg;
  123. this.literal = "";
  124. }
  125. public Term (string literal) {
  126. this.op = TermOp.None;
  127. this.arg = 0;
  128. this.literal = literal;
  129. }
  130. public string Literal {
  131. set { literal = value; }
  132. }
  133. public string GetResult (Match match) {
  134. Group group = match.Groups[arg];
  135. switch (op) {
  136. case TermOp.None:
  137. return literal;
  138. case TermOp.Match:
  139. return literal + group.Value;
  140. case TermOp.PreMatch:
  141. return literal + group.Text.Substring (0, group.Index);
  142. case TermOp.PostMatch:
  143. return literal + group.Text.Substring (group.Index + group.Length);
  144. case TermOp.All:
  145. return literal + group.Text;
  146. }
  147. return "";
  148. }
  149. public TermOp op; // term type
  150. public int arg; // group argument
  151. public string literal; // literal to prepend
  152. public override string ToString () {
  153. return op.ToString () + "(" + arg + ") " + literal;
  154. }
  155. }
  156. }
  157. }