刚才正无聊,在JavaEye的问答区Ruby分类里看到了这样一问:
[url]http://www.iteye.com/problems/3[/url]
[quote="tianshi0253"]从键盘输入一组数据,如:“测试,JAVAEYE!”,输出的结果为:“!JAVAEYE,试测”。
并且把它们存储到一个TXT文本中。 [/quote]

这简单的功能要求也挺有趣的,就尝试了一下。
基本思路是先使用String#split,然后用Array#reverse,最后用File.open和puts做输出。所以关键的地方就是要传给String#split的正则表达式参数了。

先忽略中文和全角字符,假设我们处理的都是ASCII字符。然后,那题目看来是要把某些字符看作分隔符来讲字符串分割开来,但同时要求分隔符也被保留。没有明确要求空白字符如何处理,这里假设空白字符在目标字符串中被忽略。

我们知道,给String#split的参数里的正则表达式所能匹配的子串会被“吃掉”,所以为了匹配分隔符而又不让它们被“吃掉”,我们就需要“零长度匹配”,或者叫“环视”(lookaround)。
同时使用正向和逆向环视(lookahead和lookbehind),我们就能够完成对指定的分隔符的匹配和分隔,同时在目标字符串中保留它们。所以最终的程序会类似这样:

split = ",.!?"              # 可以将别的字符定义为分隔符
file_path = "D:/result.txt" # 可以从别的地方获取输出的文件的路径
File.open(file_path, "w") do |file|
  file.puts gets.chomp.split(/\s*(?:(?<=[#{split}])|(?=[#{split}]))\s*/).reverse.join("")
end



这样,当我们执行这个程序,并且在命令行输入下面的字符串时:


[code]Hello, JavaEye![/code]


在D:\result.txt里我们就会得到:


[code]!JavaEye,Hello[/code]

差不多了吧?注意到空格被去掉了(基于上面的假设)



来仔细看看那个正则表达式(先将split带入进去):


/\s*(?:(?<=[#{split}])|(?=[#{split}]))\s*/



一头一尾都是匹配任意个数的空白字符:\s*


中间是一个非捕获型分组(? ... )


这个分组内有两个选择,前一个是逆向环视,用于匹配左边有分隔符的情况:(?<=[,.!?])


后一个是正向环视,用于匹配右边有分隔符的情况:(?=[,.!?])



这样就大致的解决了那个问题,只是……



(又有“只是”了)嗯,用Ruby 1.8.x来执行上面的代码是行不通的。因为Ruby采用的正则表达式引擎是自己实现的,而不是PCRE或者Perl自身用的那个,不支持逆向环视。


幸好,在Ruby 1.9.0里,正则表达式的引擎换成了Oniguruma(鬼車),总算支持了逆向环视。前面的代码的运行就是在Ruby 1.9.0-0上测试的。



但是……


还有一个重要的问题暂时被忽略了:中文和全角符号的支持。


如果是以UNICODE为内部编码表示的语言,那这根本不是问题;所以像是C#啊Java之类的都不会碰到什么问题。但是Ruby直到1.8.6为止对UTF-8/UTF-16/Shift-JIS以外的编码的支持都挺糟糕的。Windows下的控制台一般是设置到操作系统的默认locale,在简体中文Windows上就是GBK。这样输入到Ruby解释器里的中文字符串就可能有问题



假如把上面代码中的分隔符设置为",!"(注意到是全角字符),然后保存为UTF-8的源码文件,那么运行的时候会出错:


[code]split.rb:4:in `split': incompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (ArgumentError) 

 from split.rb:4:in `block in <main>' 

 from split.rb:3:in `open' 

 from split.rb:3:in `<main>'[/code]

假如还是保存为ANSI(简体中文Windows上也就是GBK),那么运行的结果就不太对:


[code]。JavaEye测试[/code]



我也不知道有什么好办法来解决这个问题……GUI程序的话说不定还有点办法,控制台程序真是一点办法都没有(总不能要求用户为了这么个小程序去调整控制台编码吧 = =)


平时自己写的小程序多半都只用英文,就避开了这个问题。但是要是写给别人用,而又非要用中文的话,那我还真的不知道怎么办好了。Sigh。