考虑一下我们经常遇到的问题,比如gemfield想从青岛之光读书(www.civilnet.cn/book)中找一个关键的电话号码,通常第一步就是将书中所有的电话号码查找出来放在手边。那么怎么拟定查询条件呢?电话的格式有如下几种:
01088888888
010 88888888
010-88888888
88888888
0532-88888888
0534-8888888
88888888beijing
qingdao88888888
…………
省略号的意思是格式的种类有很多种,但也有更多种明显就不是电话类型。这种情况下如何拟定我们的查询条件呢。如果面面俱到的话,代码中得多少次if或者switch分支呢。
Gemfield此刻是多么的希望有一个语句能够简单的描述上述所有可能的格式。这或许就是正则表达式的来历。正则表达式的英文原意是:Regular Expression。Regular Expression的“Regular”一般被译为“正则”、“正规”、“常规”。此处的“Regular”即是“规则”、“规律”的意思,Regular Expression即“描述某种规则的表达式”之意。
来看看gemfield如何一步步实现这个想法。
我们可以用
ddd-dddddddd或者ddddddddwwwwwww
来分别描述类似于010-88888888和88888888beijing这样的例子。
这里,d表示所有可能出现的数字:0、1、2、3、4…… 9;w表示所有可能出现的字母:a、b、c、d……z。
但有时我们在这个位置上明确的限定就是’w’这个字母,而非代表所有字母的w,那怎么办呢?上面的举例肯定会带来混淆。我们借助反斜杠/来实现:
/d/d/d-/d/d/d/d/d/d/d/d或者/d/d/d/d/d/d/d/d/w/w/w/w/w/w/w
这样就能区分w到底是代表所有字母,还是仅仅代表w本身这个字母。不过看起来就有点丑陋了,这么长,输错或者看错的个数怎么办?gemfield可以使用下面的写法来使其变得简洁:
/d3-/d8或者/d8/w7
但问题又来了,我们这里出现的3是表示前面的数字出现3次,而非3本身,后面的8、7等数字同理。怎么办呢?gemfield可以这样写:
/d{3}-/d{8}或者/d{8}/w{7}
这里{}的意义和反斜杠/差不多。都是明确告诉你,w不是w,3不是3。:-D
但很明显,有的电话号是不加区号的,比如010-88888888写作88888888。这样,就不是/d{3}-/d{8}了,而是/d{8}。或许你可以写作:
/d{8}或者/d{3}-/d{8}
但“或者”不可能成为程序语言中的关键字的,没有编译器会认识他。怎么办?我们想到了程序中有个运算符意思和“或者”相似,它就是| 。gemfield可以写作:
/d{8} | /d{3}-/d{8}
但怎么看怎么都像少个东西,看起来混在一起分辨不清。我们想到了圆括号:
(/d{8})|(/d{3}-/d{8})
用圆括号括起来就表示一个整体了,这里的意思就是说,有2组模式,其中的哪一种都可以。传统上,?这个符号(问号)可以用来表示某项是可选的。这样的话,上面的表达式可以写作:
(/d{3}-)?(/d{8})
这样就表示前面的那一组(/d{3}-)是可选的,也即既可以有,也可以不要,这刚好表达了gemfield的本意。由于还有010 88888888这种形式,我们还要考虑空格的可选,对于空格,我们可以用/s来表示。表达式修正如下:
(/d{3})?(-)?/s?(/d{8})
但是电话号码010 88888888中间的空格在输入的时候也许多输了一个,比如
10 88888888,那怎么办呢?我们用*来表示0个、1个、2个或者多个。修正如下:
(/d{3})?(-)?/s*(/d{8})
但是,似乎好像电话不都像北京一样,有8个数字(除了区号),大多数中国城市还是7个呢。所以这个数量也必须是可选的。我们可以扩展一下:
(/d{3})?(-)?/s*(/d{7,8})
这样就表示电话号码是7个或者8个,也就是大于等于7,小于等于8。
好了,虽然上面的演化对于严谨的语法来说没有什么意义,但有了这个思路,就可以认识我们的QregExp了,欢迎来到Qt的正则表达式——QregExp。
****************************************************
正则表达式由语句、数量、界定符三者组成。
语句是最简单的,由[]括起来一个完整的子语句。如[ABCD] 匹配字母A或B或C或D,而[A-Z]表示26个大写的英文字母。
A{1,26}匹配1个、2个、3个……26个字母A;
[0-9]{1,2}匹配0~99,但同时也匹配ab34、a34b等;
^[0-9]{1,2},匹配34bc、26abcd,只要数字前面别有其他东西;
[0-9]{1,2}$ 匹配ab65、aaaaa56,只要数字后面别有其他东西;
^[0-9]{1,2}$ 只能是2位数字了。
但是^一旦出现在方括号中就不一样了。它表示“不包含”。
例如:[^abc]匹配所有的东西,除了a或b或c。
+表示至少出现一次,如([abc]+)表示a或者b或者c至少出现一次。
至于*、?等界定符的意思,和gemfield文中初始部分的推理是一个意思。
******************************************************
而QregExp这个类是怎样使用这些regexp呢,gemfield总结大致有2种情况,这两种情况刚好是事物的两面。第一种是“检索”类的。看个例子:
******************************************************
str = "CIVILNET Corporation/tcivilnet.cn/tGELE";
QString company, web, country;
rx.setPattern("^([^/t]+)/t([^/t]+)/t([^/t]+)$");
if (rx.indexIn(str) != -1) {
company = rx.cap(1);
web = rx.cap(2);
country = rx.cap(3);
}
******************************************************
正如上面这个代码片段所揭示的,indexIn和cap这两个函数是比较常用的,至于具体的含义,可以阅读Qt在线文档:http://www.civilnet.cn/book/embedded/GUI/Qt_assistant/index.php
第二种是“禁止”类的,比如一个QlineEdit里禁止输入一些东西,比如邮箱名禁止输入&等。这个是用QRegExpValidator 来实现的,该类接收一个QregExp型的正则表达式作为实例化时的参数:
**********************************************************
QRegExp rx("-?//d{1,3}");
QValidator *validator = new QRegExpValidator(rx, this);
QLineEdit *edit = new QLineEdit(this);
edit->setValidator(validator);
************************************************************
分享到:
2011-06-20 14:01
浏览 825
评论