Qt中的正则表达式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wyy626562203/article/details/82746550

Qt中的正则表达式

RegExp类使用正则表达式提供模式匹配。
正则表达式或“regexp”是用于匹配文本中的子串的模式。这在许多情况下都很有用,例如,

描述
验证 正则表达式可以测试子字符串是否符合某些条件,例如:是一个整数或不包含空格。
搜索 正则表达式提供比简单子字符串匹配更强大的模式匹配,例如匹配mail, letter和correspondence中的一个,但不包括email, mailman, mailer, letterbox等单词。
搜索和替换 正则表达式可以用不同的子字符串替换所有出现的子字符串,例如,用&替换所有出现的&。除了&之前已经有&。
字符串拆分 正则表达式可用于识别字符串应分开的位置,例如,拆分制表符分隔的字符串。

简要介绍了正则,Qt的正则语言,一些例子以及函数文档本身的描述。 QRegExp以Perl的正则语言为模型。它完全支持Unicode。 QRegExp也可以用于更简单的通配符模式,类似于命令shell中的功能。可以使用setPatternSyntax()更改QRegExp使用的语法规则。特别是,模式语法可以设置为QRegExp::FixedString,这意味着要匹配的模式被解释为普通字符串,即特殊字符(例如反斜杠)不被转义。
关于正则表达式的优秀文本是由Jeffrey E.F.Friedl编写的正则表达式(第三版),ISBN 0-596-52812-4。
注意:在Qt 5中,新的QRegularExpression类提供了正则表达式的Perl兼容实现,建议使用QRegExp

介绍

正则表达式是由表达式,量词和断言构建的。最简单的表达是一个字符,例如x或5.表达式也可以是用方括号括起来的一组字符。 [ABCD]将匹配A或B或C或D.我们可以将此表达式写为[A-D],并且匹配英语字母表中任何大写字母的表达式将写为[A-Z]。

量词指定必须匹配的表达式的出现次数。 x {1,1}表示匹配一个且只匹配一个x。 x {1,5}表示匹配包含至少一个x但不超过五个的x个字符序列。

请注意,通常,正则不能用于检查平衡括号或标记。例如,如果标签未嵌套,则可以编写正则表达式以匹配开始html 及其结束,但如果标签嵌套,则相同的正则表达式将匹配用错误的结束打开tags。对于片段boldbolder ,第一个将匹配第一个,这是不正确的。但是,可以编写正确匹配嵌套括号或标记的正则表达式,但前提是嵌套级别的数量是固定且已知的。如果嵌套级别的数量不固定且不知道数量,则无法编写正确的正则表达式。

假设我们想要一个正则表达式来匹配0到99范围内的整数。至少需要一个数字,所以我们从表达式[0-9] {1,1}开始,它只匹配一个数字一次。 此正则表达式匹配0到9范围内的整数。要匹配最大为99的整数,请将最大出现次数增加到2,因此正则表达式变为[0-9] {1,2}。 此正则表达式满足匹配从0到99的整数的原始要求,但它也将匹配出现在字符串中间的整数。 如果我们希望匹配的整数是整个字符串,我们必须使用锚断言,^(插入符号)和(美元)。 当^是正则表达式中的第一个字符时,表示正则表达式必须与字符串的开头匹配。 当$是正则表达式的最后一个字符时,表示正则表达式必须与字符串的结尾匹配。 正则表达式变为^ [0-9] {1,2} $。 请注意断言,例如 ^$,不匹配字符。

如果您已经看过其他地方描述的正则表达式,它们可能与此处显示的不同。 这是因为一些字符集和一些量词是很常见,以至于它们被赋予特殊符号来表示它们。 [0-9]可以用符号\d代替。 准确匹配一个出现的量词{1,1}可以用表达式本身替换,即x{1,1}与x相同。 所以我们的0到99匹配器可以写成^\d{1,2}$。 它也可以写成^\d\d{0,1}$,即从字符串的开头,匹配一个数字,紧接着是0或1位数。 在实践中,它将被写为^\d\d?$。 的 是量词{0,1}的简写,即0或1次出现。 使表达式可选。^\d\d?$表示从字符串的开头,匹配一个数字,紧接着是0或1个数字,紧接着是字符串的结尾。

要编写与“mail”或“letter”或“correspondence”之类的单词匹配但与包含这些单词的单词不匹配的正则表达式,例如“email”,“mailman”,“mailer”和“letterbox”,从匹配’mail’的正则表达式开始。正则表达式是m {1,1} a {1,1} i {1,1} l {1,1},但由于字符表达式由{1,1}自动量化,我们可以简化mail,即’m’后跟’a’后跟’i’后跟’l’。现在我们可以使用竖线条|,或者包括另外两个单词,因此我们用于匹配三个单词中的任何一个的正则表达式成为mail|letter|correspondence。匹配’mail’或’letter’或’correspondence’。虽然此正则表达式将匹配我们想要匹配的三个单词之一,但它也会匹配我们不想匹配的单词,例如“email”。为了防止正则表达式匹配不需要的单词,我们必须告诉它在单词边界处开始和结束匹配。首先,我们将正则表达式括放在括号中(mail|letter|correspondence)。括号将表达式组合在一起,它们标识了我们希望捕获的正则表达式的一部分。将表达式括在括号中允许我们将它用作更复杂的正则中的组件。它还允许我们检查实际匹配的三个单词中的哪一个。为了强制匹配开始和结束字边界,我们将正则表达式括在\b字边界断言中:\b(mail|letter|correspondence)\b。现在正则表达式意味着:匹配一个单词边界,然后是括号中的正则表达式,后跟一个单词边界。 \b断言匹配正则表达式中的位置,而不是字符。字边界是任何非字字符,例如,空格,换行符或字符串的开头或结尾。

如果我们想用HTML实体&替换&符号,匹配的正则表达式就是&。 但是这个正则表达式也将匹配已经转换为HTML实体的&符号。 我们想要仅替换后面没有跟amp;符号。 为此,我们需要负前瞻断言(?!__)。 然后可以将正则表达式写为&(?!amp;),即匹配没有跟amp;符号。
如果我们想要计算字符串中所有’Eric’和’Eirik’的出现次数,那么两个有效的解决方案是\b(Eric|Eirik)\b\bEi?ri[ck]\b。 需要使用单词边界断言'\b'来避免匹配包含任一名称的单词,例如“Ericsson”。 请注意,第二个正则表达式匹配的更多:’Eric’,’Erik’,’Eiric’和’Eirik’。
上面讨论的一些示例在代码示例部分中实现。

字符和字符集的缩写

元素 描述
c 除非具有特殊的正则表达式含义,否则代表自身。 例如 c匹配字符c。
\c 匹配字符,但下面指定的除外。 例如,要匹配字符串开头的文符,请写入^
\a 匹配ASCII铃声(BEL,0x07)。
\f 匹配ASCII换页符(FF,0x0C)。
\n 匹配ASCII换行符(LF,0x0A,Unix换行符)。
\r 匹配ASCII回车符(CR,0x0D)。
\t 匹配ASCII水平制表符(HT,0x09)。
\v 匹配ASCII垂直制表符(VT,0x0B)。
\xhhhh 匹配对应于十六进制数hhhh(在0x0000和0xFFFF之间)的Unicode字符。
\0ooo (i.e., \zero ooo) 匹配八进制数ooo的ASCII / Latin1字符(介于0和0377之间)。
. (dot) 匹配任何字符(包括换行符)。
\d 匹配一个数字(QChar::isDigit())。
\D 匹配非数字。
\s 匹配空白字符(QChar::isSpace())。
\S 匹配非空白字符。
\w 匹配单词字符(QChar::isLetterOrNumber()QChar::isMark()'_')。
\W 匹配非单词字符。
\n 第n个反向引用,例如 \1,\2等

注意:C++编译器会在字符串中转换反斜杠。 要在正则表达式中包含\,请输入两次,即\\。 要匹配反斜杠字符本身,请输入四次,即\\\\

字符集

方括号表示匹配方括号中包含的任何字符。 上述字符集缩写可以出现在方括号中。 除字符集缩写和以下两个例外外,字符在方括号中没有特殊含义。

描述
^ 插入符号如果作为第一个字符出现(即紧接在开始方括号之后),则否定字符集。 [abc]匹配’a’或’b’或’c’,但[^abc]匹配除“a”或“b”或“c”之外的任何内容。
- 短划线表示一系列字符。 [W-Z]匹配’W’或’X’或’Y’或’Z’。

使用预定义字符集缩写比使用跨平台和语言的字符范围更便携。 例如,[0-9]匹配西方字母表中的数字,但\d匹配任何字母表中的数字。
注意:在其他正则表达式文档中,字符集通常称为“字符类”。

量词

默认情况下,表达式由{1,1}自动量化,即它应该恰好出现一次。 在以下列表中,E代表表达式。表达式是一个字符,或一组字符的缩写,或方括号中的一组字符,或括号中的表达式。

描述
E? 匹配零次或一次出现的E.此量词表示前一个表达式是可选的,因为它将匹配是否找到表达式。E? 与E {0,1}相同。 例如,凹痕? 匹配’凹痕’或’凹痕’。
E+ 匹配一次或多次出现的E. E+E {1,}相同。 例如,0+匹配’0’,’00’,’000’等。
E* 匹配零次或多次出现的E.它与E{0,}相同。 *量词通常会应用错误,其中应使用+。 例如,如果在表达式中使用\s*$来匹配以空格结尾的字符串,则它将匹配每个字符串,因为\s*$表示匹配零个或多个空格结尾的字符串。 正确的正则匹配至少有一个尾随空白字符的字符串是\s+$
E{n} 匹配恰好n次出现E. E {n}与重复E n次相同。 例如,x{5}与xxxxx相同。 它也与E{n,n}相同,例如X{5,5}
E{n,} 匹配至少n次出现的E.
E{,m} 匹配最多m次出现的E. E{,m}E{0,m}相同。
E{n,m} 匹配至少n次且最多m次出现的E.

要将量词应用于整个字符串,请使用括号在表达式中将字符组合在一起。 例如,tag +匹配’t’后跟’a’后跟至少一个’g’,而(tag)+匹配至少一次'tag'匹配。
注意:量词通常是“贪婪的”。 它们总是匹配尽可能多的文本。 例如,0+匹配它找到的第一个零和第一个零之后的所有连续零。 适用于’20005’,它与’20005’相匹配。 量词可以变得非贪婪,请参阅setMinimal()。

捕获文本

括号允许我们将元素组合在一起,以便我们可以量化和捕获它们。例如,如果我们有与字符串匹配的表达式mail|letter|correspondence关系,我们知道其中一个单词匹配但不知道是哪一个。使用括号允许我们“捕获”其边界内匹配的任何内容,因此如果我们使用(mail | letter | Correspon)并将此正则与字符串“I sent you some email”相匹配,我们可以使用cap()capturedTexts( )用于提取匹配字符的函数,在本例中为“mail”。
我们可以在正则本身中使用捕获的文本。为了引用捕获的文本,我们使用从1开始索引的反向引用,与cap()相同。例如,我们可以使用\b(\w+)\W+\1\b搜索字符串中的重复单词,这意味着匹配单词边界,后跟一个或多个单词字符,后跟一个或多个非单词字符,后跟相同的单词字符text作为第一个带括号的表达式,后跟一个单词边界。
如果我们想要将括号纯粹用于分组而不是用于捕获,我们可以使用非捕获语法,例如 (?:green|blue)。非捕获括号开始'(?:'and end')'。在这个例子中,我们匹配’green’或’blue’但我们不捕获匹配,所以我们只知道我们是否匹配但不知道我们实际找到的颜色。使用非捕获括号比使用捕获括号更有效,因为正则表达式引擎必须减少记录。

捕获和非捕获括号都可以嵌套。
由于历史原因,适用于捕获括号的量词(例如)比其他量词更“贪婪”。 例如,a (a *)将匹配“aaa”与cap(1)==“aaa”。 此行为与其他正则表达式引擎不同(特别是Perl)。 要获得更直观的捕获行为,请将QRegExp::RegExp2指定给QRegExp构造函数或调用setPatternSyntax(QRegExp::RegExp2)
当无法预先确定匹配数时,常见的习惯用法是在循环中使用cap()。 例如:

QRegExp rx("(\\d+)");
QString str = "Offsets: 12 14 99 231 7";
QStringList list;
int pos = 0;

while ((pos = rx.indexIn(str, pos)) != -1) {
    list << rx.cap(1);
    pos += rx.matchedLength();
}
// list: ["12", "14", "99", "231", "7"]

断言

断言在正则表达式中出现的位置对语句进行了一些陈述,但它们与任何字符都不匹配。 在下面的列表中,E代表任何表达式。

描述
^ 插入符号表示字符串的开头。 如果你想匹配文字^你必须通过写\^来转义它。 例如,^#include仅匹配以字符’#include’开头的字符串。 (当插入符号是字符集的第一个字符时,它具有特殊含义,请参见字符集。)
$ 表示字符串的结尾。 例如\d\s*$将匹配以数字结尾的字符串,可选地后跟空格。 如果你想匹配字符$,你必须通过写\\$来转义它。
\b 一个单词边界。 例如,正则\bOK\b意味着在单词边界(例如字符串或空格的开头)之后立即匹配字母“O”,然后紧接在另一个字边界之前的字母“K”(例如字符串或空格的结尾)。 但请注意,断言实际上并不匹配任何空格,所以如果我们写(\bOK\b)并且我们有匹配,它将只包含’OK’,即使该字符串是“It’s OK now”。
\B 非字符边界。 如果\b为假,则该断言为真。 例如,如果我们在“Left on”中搜索\Bon\B,则匹配将失败(字符串的空格和结尾不是非字边界),但它将匹配“tonne”。
(?=E) 如果表达式在正则中匹配,则此断言为true。 例如,const(?=\s+char)只要跟随’char’就匹配’const’,如’static const char ‘。 (与const\s + char比较,它匹配’static const char ‘。)
(?!E) 如果表达式在正则表达式中此时不匹配,则此断言为真。 例如,const(?!\s+char)匹配’const’,除非它后跟’char’。

通配符匹配

大多数命令shell(如bash或cmd.exe)都支持“文件通配符”,即使用通配符标识一组文件的功能。setPatternSyntax()函数用于在正则表达式和通配符模式之间切换。 通配符匹配比完整正则表达式简单得多,并且只有四个功能:

描述
c 除下面提到的那些之外,任何角色都代表自己 因此c匹配字符c。
? 匹配任何单个字符。 它是一样的。 完全正则表达式。
* 匹配零个或多个任意字符。 完全正则表达式与。*相同。
[…] 字符集可以用方括号表示,类似于完整的正则表达式。 在字符类中,像外部一样,反斜杠没有特殊含义。

在通配符模式中,无法转义通配符。 在WildcardUnix模式中,字符'\'转义通配符。
例如,如果我们处于通配符模式并且包含文件名的字符串,我们可以使用*.html识别HTML文件。 这将匹配零个或多个字符,后跟一个点后跟’h’,’t’,’m’和’l’。
要针对通配符表达式测试字符串,请使用exactMatch()。 例如:

QRegExp rx("*.txt");
rx.setPatternSyntax(QRegExp::Wildcard);
rx.exactMatch("README.txt");        // returns true
rx.exactMatch("welcome.txt.bak");   // returns false

Perl用户注意事项

Perl支持的大多数字符类缩写都受QRegExp支持,请参阅字符和字符集的缩写。
在QRegExp中,除了字符类之外,^始终表示字符串的开头,因此必须始终转义插入符号,除非用于此目的。 在Perl中,插入符的含义根据其发生的位置自动变化,因此很少需要它。 这同样适用于$,在QRegExp中始终表示字符串的结尾。
QRegExp的量词与Perl的贪心量词相同(请参见上面的注释)。 非贪婪匹配不能应用于单个量词,但可以应用于模式中的所有量词。 例如,要匹配Perl regexp ro +?m,需要:

QRegExp rx("ro+m");
rx.setMinimal(true);

Perl的/i选项的等价物是setCaseSensitivity(Qt::CaseInsensitive)
可以使用循环模拟Perl的/g选项。
在QRegExp中。匹配任何字符,因此所有QRegExp正则表达式都相当于Perl的/s选项。 QRegExp没有等效于Perl的/m选项,但可以通过各种方式进行模拟,例如将输入拆分为行或通过使用搜索换行符的正则表达式循环。
因为QRegExp是面向字符串的,所以没有\A\Z\z断言。\G断言不受支持,但可以循环模拟。
Perl的$&cap(0)capturedTexts()[0]。 **$**,**$'**或**$+**没有QRegExp等价物。 Perl的捕获变量,$ 1,$ 2,...对应cap(1)capturedTexts()[1]cap(2)capturedTexts()[2]等。
要替换模式,请使用
QString::replace()
不支持Perl的扩展
/x`语法,也不支持指令,例如(?i)或regexp注释,例如(?#comment)。另一方面,C++的文字字符串规则可用于实现相同的目的:

 QRegExp mark("\\b"      // word boundary
                "[Mm]ark" // the word we want to match
              );

使用与Perl相同的语法支持(?=pattern)(?!pattern)。 Perl的lookbehind断言,“独立”子表达式和条件表达式不受支持。
还支持非捕获括号,具有相同的 (?:pattern)语法。
有关Perl的拆分和连接函数的等价物,请参阅QString::split()QStringList::join()
注意:因为C++转换它们必须在代码中写入两次,例如\b必须写成\\b

代码示例

QRegExp rx("^\\d\\d?$");    // match integers 0 to 99
rx.indexIn("123");          // returns -1 (no match)
rx.indexIn("-6");           // returns -1 (no match)
rx.indexIn("6");            // returns 0 (matched at position 0)

第三个字符串匹配’6’。 这是0到99范围内整数的简单验证正则表达式。

QRegExp rx("^\\S+$");       // match strings without whitespace
rx.indexIn("Hello world");  // returns -1 (no match)
rx.indexIn("This_is-OK");   // returns 0 (matched at position 0)

第二个字符串匹配’This_is-OK’。我们使用字符集缩写'\S'(非空格)和锚来匹配不包含空格的字符串。
在下面的示例中,我们匹配包含’mail’或’letter’或’Correspon’的字符串,但仅匹配整个单词,即不匹配’email’

QRegExp rx("\\b(mail|letter|correspondence)\\b");
rx.indexIn("I sent you an email");     // returns -1 (no match)
rx.indexIn("Please write the letter"); // returns 17

第二个字符串匹配“Please write the letter”。 还会捕获“letter”一词(因为括号)。 我们可以看到我们捕获的文本是这样的:

QString captured = rx.cap(1); // captured == "letter"

这将捕获第一组括号中的文本(计算从左到右捕获左括号)。 括号从1开始计数,因为cap(0)是整个匹配的regexp(相当于大多数regexp引擎中的’&’)。

QRegExp rx("&(?!amp;)");      // match ampersands but not &amp;
QString line1 = "This & that";
line1.replace(rx, "&amp;");
// line1 == "This &amp; that"
QString line2 = "His &amp; hers & theirs";
line2.replace(rx, "&amp;");
// line2 == "His &amp; hers &amp; theirs"

这里我们将QRegExp传递给QString的replace()函数,用新文本替换匹配的文本。

QString str = "One Eric another Eirik, and an Ericsson. "
    "How many Eiriks, Eric?";
QRegExp rx("\\b(Eric|Eirik)\\b"); // match Eric or Eirik
int pos = 0;    // where we are in the string
int count = 0;  // how many Eric and Eirik's we've counted
while (pos >= 0) {
    pos = rx.indexIn(str, pos);
    if (pos >= 0) {
        ++pos;      // move along in str
        ++count;    // count our Eric or Eirik
    }
}

我们使用indexIn()函数重复匹配字符串中的正则表达式。 请注意,我们可以编写pos + = rx.matchedLength()来跳过已经匹配的字符串,而不是一次向前移动一个字符。 计数将等于3,匹配’One Eric another Eirik, and an Ericsson. How many Eiriks, Eric?, 它与“Ericsson”或“Eiriks”不匹配,因为它们不受非字符边界的限制。
在正则中的一个常见用途是将分隔数据行分割为其组件字段。

str = "The Qt Company Ltd\tqt.io\tFinland";
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);
}

在此示例中,我们的输入行具有公司名称,网址和国家/地区的格式。 不幸的是,正则表达式相当长而且不是很通用 - 如果我们添加更多字段,代码将会中断。 一个更简单和更好的解决方案是在这种情况下查找分隔符'\t',并获取周围的文本。 QString::split()函数可以将分隔符字符串或regexp作为参数,并相应地拆分字符串。

QStringList field = str.split("\t");

这里字段[0]是公司,字段[1]是网址等。
为了模仿shell的匹配,我们可以使用通配符模式。

QRegExp rx("*.html");
rx.setPatternSyntax(QRegExp::Wildcard);
rx.exactMatch("index.html");                // returns true
rx.exactMatch("default.htm");               // returns false
rx.exactMatch("readme.txt");                // returns false

由于其简单性,通配符匹配很方便的,但是可以使用完整的正则表达式来定义任何通配符正则表达式,例如.*.html . h t m l . h t m 使 \* . h t m \* t e s t . h t m l . b a k \*   . h t m l
QRegExp可以使用setCaseSensitivity()不区分大小写,并且可以使用非贪婪匹配,请参阅setMinimal()。 默认情况下,QRegExp使用完整的正则表达式,但可以使用setPatternSyntax()更改此值。 可以使用indexIn()向前搜索,也可以使用lastIndexIn()向后搜索。 可以使用captureTexts()来访问捕获的文本,captureTexts()返回所有捕获的字符串的字符串列表,或者使用cap()返回给定索引的捕获字符串。 pos()函数获取匹配索引并返回匹配所在的字符串中的位置(如果没有匹配则返回-1)。

猜你喜欢

转载自blog.csdn.net/wyy626562203/article/details/82746550