2023年3月29日 星期三

多重字串替換

這是指在一次執行中把要替換的多筆字串一次完成,哪組先找到先替換,替換過的不能再被替換。有別於多次執行替換的處理。

關於拿掉 if (!f)   return Src; 的原因在,只有 Replacements 為空時才會有作用可提早離開,並不會影響功能的正確性,絕大多數 Replacements 並不會是空的,沒必要多此一舉。

  1. #include <string>
  2. #include <functional>
  3. #include <regex>
  4. #include <unordered_map>
  5. #include <iostream>
  6.  
  7. using namespace std;
  8.  
  9. // 多重字串替換
  10. class MultipleReplace
  11. {
  12. protected:
  13. static string Regex_MultipleReplace(
  14. const string &Src,
  15. const regex &rgx,
  16. const function<string(const smatch &sm)> &fmt
  17. )
  18. {
  19. string S = Src;
  20. string R;
  21. smatch sm;
  22.  
  23. while (regex_search(S, sm, rgx))
  24. {
  25. R = R + string(S, 0, sm.position()) + fmt(sm);
  26.  
  27. S = sm.suffix().str();
  28. }
  29. return R + S;
  30. }
  31.  
  32. // 找出字串中含有正則表達式用到的特殊字元,在其前頭加上 '\' 字
  33. // 元,再將處理完的字串傳回
  34. // 這段由 AI 生成
  35. static string addSlashesToRegex(const string &str)
  36. {
  37. string specialChars = ".^$*+?()[{\\|";
  38. string result = "";
  39.  
  40. for (int i = 0; i < str.length(); i++)
  41. {
  42. if (specialChars.find(str[i]) != string::npos)
  43. {
  44. result += "\\";
  45. }
  46. result += str[i];
  47. }
  48.  
  49. return result;
  50. }
  51.  
  52. // Constructor
  53. MultipleReplace (){};
  54.  
  55. public:
  56. // 在一次執行中把要替換的多筆字串一次完成,
  57. // 哪組先找到先替換,替換過的不能再被替換。
  58. static string Replace(const string &Src,
  59. const unordered_map<string, const string>& replacements)
  60. {
  61. string rgx;
  62. bool f = false;
  63. for (const auto& replacement : replacements)
  64. {
  65. if(f)
  66. rgx += "|";
  67. rgx += "(" + addSlashesToRegex(replacement.first) + ")";
  68. f = true;
  69. }
  70.  
  71. // if(!f)
  72. // return Src;
  73.  
  74. return Regex_MultipleReplace(Src,regex(rgx),
  75. [&replacements](const smatch &sm) -> string
  76. {
  77. return const_cast<unordered_map<string, const string>&>(
  78. replacements)[sm.str()];
  79. }
  80. );
  81. }
  82.  
  83. // 為以下這個類別(前一版)增加一個 static 函數,像 Replace()
  84. // 一樣可以做多重替換,用正則表達式來進行搜索和替換
  85. // AI 生成人工優化
  86. static string RegexReplace(const string& Src,
  87. const unordered_map<string, const string>& replacements)
  88. {
  89. string rgx;
  90. bool f = false;
  91. for (const auto& replacement : replacements)
  92. {
  93. if (f)
  94. rgx += "|";
  95. rgx += "(" + replacement.first + ")";
  96. f = true;
  97. }
  98.  
  99. // if (!f)
  100. // return Src;
  101.  
  102. return Regex_MultipleReplace(Src, regex(rgx),
  103. [&replacements](const smatch& sm) -> string
  104. {
  105. string matchStr = sm.str();
  106. for (const auto& replacement : replacements)
  107. {
  108. regex rgx(replacement.first);
  109. if (regex_match(matchStr, rgx))
  110. {
  111. return replacement.second;
  112. }
  113. }
  114. return matchStr;
  115. }
  116. );
  117. }
  118.  
  119. // 融合 Replace() 和 RegexReplace()
  120. // directReplacements 直接字元搜尋替換,regexReplacements 正則表達式搜尋替換
  121. // 符合的搜尋 directReplacements 優先於 regexReplacements
  122. // 一樣一次執行中把要替換的多筆字串一次完成,哪組先找到先替換,替換過的不能再被替換
  123. static string MixReplace(const string& Src,
  124. const unordered_map<string, const string>& directReplacements,
  125. const unordered_map<string, const string>& regexReplacements)
  126. {
  127. string rgx;
  128. bool f = false;
  129. for (const auto& replacement : directReplacements)
  130. {
  131. if(f)
  132. rgx += "|";
  133. rgx += "(" + addSlashesToRegex(replacement.first) + ")";
  134. f = true;
  135. }
  136. for (const auto& replacement : regexReplacements)
  137. {
  138. if (f)
  139. rgx += "|";
  140. rgx += "(" + replacement.first + ")";
  141. f = true;
  142. }

  143. // if (!f)
  144. // return Src;
  145.  
  146. return Regex_MultipleReplace(Src, regex(rgx),
  147. [&directReplacements,&regexReplacements](const smatch& sm) -> string
  148. {
  149. string matchStr = sm.str();
  150.  
  151. string result = const_cast<unordered_map<string, const string>&>(
  152. directReplacements)[matchStr];
  153. if(!result.empty())
  154. return std::move(result);
  155.  
  156. for (const auto& replacement : regexReplacements)
  157. {
  158. regex rgx(replacement.first);
  159. if (regex_match(matchStr, rgx))
  160. {
  161. return replacement.second;
  162. }
  163. }
  164.  
  165. return std::move(matchStr);
  166. }
  167. );
  168. }
  169. };
  170.  
  171. // 請用 C++11 用以上的功能在 main() 分別為 Replace()、RegexReplace()
  172. // 和 MixReplace() 寫一個例子。要列印出例子的用意和替換前後的字串,其
  173. // 中用來 {搜尋,替換} 的組數須不一樣,但須在 2 至 10 組之間,同時讓我看到輸出結果
  174. // AI 生成
  175. int main() {
  176. // 用 Replace() 進行字串替換
  177. std::cout << "Use Replace() to replace multiple strings:" << std::endl;
  178. std::string str1 = "Replace AAA, BBB and CCC with X, Y and Z";
  179. std::cout << "Before: " << str1 << std::endl;
  180. std::unordered_map<std::string, const std::string> replacements1 = {
  181. {"AAA", "X"},
  182. {"BBB", "Y"},
  183. {"CCC", "Z"}
  184. };
  185. str1 = MultipleReplace::Replace(str1, replacements1);
  186. std::cout << "After: " << str1 << std::endl;
  187. std::cout << std::endl;
  188.  
  189. // 用 RegexReplace() 進行字串替換
  190. std::cout <<
  191. "Use RegexReplace() to replace multiple strings with regex:"
  192. << std::endl;
  193. std::string str2 = "Replace AAA, BBB and CCC with X, Y and Z using regex";
  194. std::cout << "Before: " << str2 << std::endl;
  195. std::unordered_map<std::string, const std::string> replacements2 = {
  196. {"AAA", "X"},
  197. {"\\bBBB\\b", "Y"},
  198. {"C+", "Z"}
  199. };
  200. str2 = MultipleReplace::RegexReplace(str2, replacements2);
  201. std::cout << "After: " << str2 << std::endl;
  202. std::cout << std::endl;
  203.  
  204. // 用 MixReplace() 進行字串替換
  205. std::cout <<
  206. "Use MixReplace() to replace multiple strings with both direct and regex:"
  207. << std::endl;
  208. std::string str3 =
  209. "Replace AAA, BBB and CCC with X, Y and Z using both direct and regex";
  210. std::cout << "Before: " << str3 << std::endl;
  211. std::unordered_map<std::string, const std::string> directReplacements = {
  212. {"AAA", "X"},
  213. {"BBB", "Y"}
  214. };
  215. std::unordered_map<std::string, const std::string> regexReplacements = {
  216. {"\\bCCC\\b", "Z"},
  217. {"using.*regex", "with regex"}
  218. };
  219. str3 = MultipleReplace::MixReplace(str3, directReplacements,
  220. regexReplacements);
  221. std::cout << "After: " << str3 << std::endl;
  222.  
  223. return 0;
  224. }

 

輸出結果:

Use Replace() to replace multiple strings:

Before: Replace AAA, BBB and CCC with X, Y and Z

After: Replace X, Y and Z with X, Y and Z


Use RegexReplace() to replace multiple strings with regex:

Before: Replace AAA, BBB and CCC with X, Y and Z using regex

After: Replace X, Y and Z with X, Y and Z using regex


Use MixReplace() to replace multiple strings with both direct and regex:

Before: Replace AAA, BBB and CCC with X, Y and Z using both direct and regex

After: Replace X, Y and Z with X, Y and Z with regex







沒有留言:

張貼留言