去掉[]中的英文字符

  最近在信息科技大学上传智.Net就业班的课程,中午有位信息科技大学的老师问我关于正则表达式的问题. 问题大致这么描述的:
  有一个字符串,里面是中文与英文的混排内容,中间包含几对中括号(方括号),现在有个需求,就是移除中括号中所有的英文字符,保留其他数据.
  我一看就觉得可以完成,但是随手一写发现有点问题,可能处于数学出身的原因,总想把问题尽量写得全面,我中午吃饭的时候就在考虑该怎么些比较好. 这里给出我思考这个问题的思路,看看有没有给大家一点启发.

化归思想
  数学里面有个很重要的思想,就是“化归”思想. 什么是化归呢?简单的说,就是将复杂的问题进行归纳总结,将其变成一些简单的问题的组合,然后简单问题解决了,复杂问题也就解决了.
  有朋友就会说了:“看了数学头痛,还跟我提什么化归思想… …”
  其实不然,只是化归思想这个名字很吓人——数学思想啊,其实她很基础,因为我们天天都在用这个思想. 来一个生活中的例子,想知道一个超市的商品质量和价格好与不好,怎么考究?显然这个咱们没有办法一个个检验,但是我们看得到,可以比较. 如果这个超市总是人很多,而且消费者投诉也很少,那我们就可以下结论了,这个超市不错. 再比如说,面向对象中的一个概念,C#中继承具有传递性,这个怎么理解?其实很简单,因为继承,子类包含了父类的成员,那么一直下去,孙子类继承子类,就包含了子类成员,即是孙子类包含了父类成员,因此就有了结论,派生类都具有基类成员,自然传递性就很好理解了.

问题化归
  那么说过了这么多和解决这个问题有什么关系呢?显然我解决这个问题使用了化归的思想. 关键是这个问题有什么难点呢?
我们先来看一个例子:
有一个字符串

1 string str = "abc[你abc好]abc";

现在要将字符串里面的“[你abc好]”中的英文字符“abc”去掉,使得字符串变为

1 string str = "abc[你好]abc";

显然使用正则表达式可以是

1 string str = Regex.Replace(str, @"(\[你)[a-zA-Z]+(好\])", "$1$2");

  当然这里的解答绝对不是唯一的,因此给出的只是一个我想到的方法. 但是关键是这个情况是很特殊的. 首先,方括号“[]”中不见得就是汉字在两边,而且不见得就是你好;其次,对于中间不一定就是只有一串英文字母,比如还有可能是”你abc好abc啊”,那么此时就不太管用了.
那么这里的问题便是:
  1、方括号内不确定有多少个汉子块与多少个英文块;
  2、方括号内不明确英文与汉字排列方式

  那么将问题化归一下,就好办了(注意,本文不见得给出的是最好方法,但是提供了一个思考问题的方法)

汉字两边型
  由于问题是要将方括号内的英文字母去掉,所以无论方法括号内两端是什么,都可以使用C#语句:

1 string str = Regex.Replace(str, @"(\[)[a-zA-Z]*(.+?)[a-zA-Z]*(\])", "$1$2$3");

  使得字符串中方括号两边变成汉字.
  也就是说所有的字符串都可以划归为如下模型

…[中…中]…

 

[中a中a中]型

  将字符串变为“汉字两边型”后,问题就是不明确两端汉字中有多少组引文字符,但是自己分析一下,去掉右边的汉字,那么字符串将变成由字符串片段“中a”的一个重复的循环组合,即:”中a”、”中a中a”、”中a中a中a”、… …
  那么问题就简单了,只要很好的解决“[中a中a中]型”的字符串,那么其他情况就可以利用循环解决了.
  第一步,从左往右依次去掉第一组英文(当然需要判断是否存在),例如,将一个的字符串”[中a中a中a中]”通过正则表达式替换变为”[中a中a中]”. 可以使用C#语句

1 if(Regex.IsMatch(str, @"\[[^\]]+?[a-zA-Z]+[^\]]+\]"))
2 {
3 string str = Regex.Replace(str, @"(\[[^\]]+?)[a-zA-Z]+([^\]]+\])", "$1$2");
4 }

  第二步,就很简单了,剩下的就是重复了,直到剩下“[中a中]型”. 实际上,当剩下该模式字符串时,依旧调用上面的方法,一样能实现将中间的英文去掉.

分析一下匹配规则
  实际上现在已经把问题解决了,下面我说明一下我为何将正则表达式写成这样. 我以最后一次匹配为例. 

1 string str = "a[中a中]a";
2 if(Regex.IsMatch(str, @"\[[^\]]+?[a-zA-Z]+[^\]]+\]"))
3 {
4 string str = Regex.Replace(str, @"(\[[^\]]+?)[a-zA-Z]+([^\]]+\])", "$1$2");
5 }

分析:
1、此时if判断中IsMatch判断是否匹配
  IsMatch中的正则表达式这么写“@”\[[^\]]+?[a-zA-Z]+[^\]]+\]””,表示先匹配中括号“[”开头,然后中间不允许有中括号结束的字符“]”,并且使用了取消贪婪模式使之后面的“[a-zA-Z]+”尽可能的匹配第一组英文字母. 这里匹配了str中的“[中a”. 然后剩下的“[^\]]+\]”匹配第一组引文字母结束后的所有字符,以及结束的中括号“]”. 因此返回的肯定是true.
2、开始替换
  有了if的匹配以后,替换就很容易了,Replace方法中的正则表达式中首尾两个组分别匹配“[中”和“中]”,因此,“$1$2”就将中间的引文字母替换掉了.
  对于非最后一次匹配的情况,例如替换”[中a中a中]”,组1匹配“[中”,组2匹配“中a中]”,只是将左边的第一组英文替换掉了.

整合结果

  通过前面的分析,想要将中括号里面的英文去掉,可采用的通用思维为
-> 先去掉中括号内首尾英文字母
-> 在利用循环,从左往右依次去掉一组组英文字符,直到英文字符为止

  结合前面的讨论,给出一个方法

1 public static string MyReplace(string str)
2 {
3 str = Regex.Replace(str, @"(\[)[a-zA-Z]*(.+?)[a-zA-Z]*(\])", "$1$2$3");
4 while (Regex.IsMatch(str, @"\[[^\]]+?[a-zA-Z]+[^]]+\]"))
5 {
6 str = Regex.Replace(str, @"(\[[^\]]+?)[a-zA-Z]+([^\]]+\])", "$1$2");
7 }
8 return str;
9 }

  但是值得思考的是,此处不一定是绝对的解决方案,按照我的思维,一定会有更好的方法出现. 这里我只是给出了一个思考问题的方法,并拟定出了解决方案.

                                        2012年3月23日星期五 蒋坤










原文地址:https://www.cnblogs.com/jiangkun86/p/2413027.html