Qt:QRegExp类使用正则表达式提供模式匹配

简要介绍了regexp,Qt的regexp语言,一些例子以及函数文档本身的描述。 QRegExp以Perl的regexp语言为模型。 它完全支持Unicode。 QRegExp也可以用于更简单的通配符模式,类似于命令shell中的功能。 可以使用setPatternSyntax()更改QRegExp使用的语法规则。 特别是,模式语法可以设置为QRegExp :: FixedString,这意味着要匹配的模式被解释为普通字符串,即特殊字符(例如反斜杠)不被转义。

关于正则表达式的优秀文本是由Jeffrey E.F.Friedl编写的正则表达式(第三版),ISBN 0-596-52812-4。

regexp是由表达式、量词和断言构建的。最简单的表达式是字符,例如x或5。表达式也可以是方括号内的一组字符。[ABCD]将匹配A、B、C或D。我们可以将这个表达式写成[A- D],而与英语字母表中任何大写字母匹配的表达式写成[A- Z]。

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

注意,一般情况下,regexp不能用于检查平衡的括号或标记。例如,如果<b>标记没有嵌套,可以编写regexp来匹配html开始的 <b>及其结束的</b>; 但是如果<b>标记是嵌套的,那么相同的regexp将位开始的<b>标记匹配一个错误的结束</b>。对于片段<b>bold <b>bold </b></b>,第一个<b>将与第一个</b>匹配,这是不正确的。只有在嵌套级别的数量固定且已知的情况下,才有可能编写正确匹配嵌套方括号或标记的regexp。如果嵌套级别的数量不固定且已知,那么编写的regexp一定会失败。

假设我们希望regexp匹配范围为0到99的整数。至少需要一个数字,所以我们从表达式[0-9]{1,1}开始,它恰好匹配一个数字一次。这个regexp匹配范围为0到9的整数。要匹配最多99个整数,请将最大出现次数增加到2,这样regexp就变成[0-9]{1,2}。这个regexp满足从0到99匹配整数的原始要求,但它也将匹配出现在字符串中间的整数。如果我们想要匹配整个字符串表示一个整数的情况,我们必须使用锚断言,^(脱字符号)和$ (dollar)。当一个regexp的第一个字符为^ ,意味着regexp必须从string的第一个字符开始匹配。当$是regexp的最后一个字符时,这意味着regexp必须匹配到字符串的末尾。于是regexp变为^ [0 - 9]{1,2}$。注意,断言,例如^和$本身不匹配string中的字符,而只是说明匹配位置。

如果您在其他地方看到过对regexp的描述,那么它们看起来可能与这里显示的不同。这是因为一些字符集和一些量词是如此常见,以至于它们被赋予了特殊的符号来表示它们。[0-9]可以用符号\d替换。x { 1 1 }和 x 是一样的。所以我们的0到99匹配器可以写成^ \ d { 1,2 } $。也可以写^ \d \ d { 0,1 } $,即从一开始的字符串,匹配一个数字,然后接着匹配0或1个数字。在实践中,它将被写成^ \ d\ d ?$。?是量词{0,1}的简写,即出现0或1次。?使表达式可选。regexp ^ \ d\ d ?$表示从字符串的开头匹配一个数字,紧接着匹配0或1个数字。

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

如果我们想用&amp来替换&,我们可以用regexp去匹配&,但这样也会同时匹配其后已经有amp的&字符,而我们只想将其后不是amp的&替换为&amp。为此我们需要消极的前瞻性断言 , (?!_)。regexp可以写为&(?!amp;),即匹配其后没有amp跟随着的&字符。

如果我们想计数字符串中出现的所有'Eric'和'Eirik',两个有效的解决方案是\b(Eric|Eirik)\b 和 \bEi?ri[ck]\b。需要单词边界断言'\b'来避免匹配包含这两个名称中的任何一个单词的单词,例如。“'Ericsson'”。注意,第二个regexp匹配的拼写比我们希望的要多:“Eric”、“Erik”、“Eiric”和“Eirik”。

上面讨论的一些示例在code examples部分中实现。

Characters and Abbreviations for Sets of Characters

Element

Meaning

c

A character represents itself unless it has a special regexp meaning. e.g. c matches the character c.

\c

A character that follows a backslash matches the character itself, except as specified below. e.g., To match a literal caret at the beginning of a string, write \^.

\a

Matches the ASCII bell (BEL, 0x07).

\f

Matches the ASCII form feed (FF, 0x0C).

\n

Matches the ASCII line feed (LF, 0x0A, Unix newline).

\r

Matches the ASCII carriage return (CR, 0x0D).

\t

Matches the ASCII horizontal tab (HT, 0x09).

\v

Matches the ASCII vertical tab (VT, 0x0B).

\xhhhh

Matches the Unicode character corresponding to the hexadecimal number hhhh (between 0x0000 and 0xFFFF).

\0ooo (i.e., \zero ooo)

matches the ASCII/Latin1 character for the octal number ooo (between 0 and 0377).

. (dot)

Matches any character (including newline).

\d

Matches a digit (QChar::isDigit()).

\D

Matches a non-digit.

\s

Matches a whitespace character (QChar::isSpace()).

\S

Matches a non-whitespace character.

\w

Matches a word character (QChar::isLetterOrNumber(), QChar::isMark(), or '_').

\W

Matches a non-word character.

\n

The n-th backreference, e.g. \1, \2, etc.

注意:c++编译器转换字符串中的反斜杠。要在regexp中包含\,请输入它两次,即\\。要匹配反斜杠字符本身,请输入4次,即\\\\。

Quantifiers(量词)

E?

Matches zero or one occurrences of E. This quantifier means The previous expression is optional, because it will match whether or not the expression is found. E? is the same as E{0,1}. e.g., dents? matches 'dent' or 'dents'.

E+

Matches one or more occurrences of E. E+ is the same as E{1,}. e.g., 0+ matches '0', '00', '000', etc.

E*

Matches zero or more occurrences of E. It is the same as E{0,}. The * quantifier is often used in error where + should be used. For example, if \s*$ is used in an expression to match strings that end in whitespace, it will match every string because \s*$ means Match zero or more whitespaces followed by end of string. The correct regexp to match strings that have at least one trailing whitespace character is \s+$.

E{n}

Matches exactly n occurrences of E. E{n} is the same as repeating E n times. For example, x{5} is the same as xxxxx. It is also the same as E{n,n}, e.g. x{5,5}.

E{n,}

Matches at least n occurrences of E.

E{,m}

Matches at most m occurrences of E. E{,m} is the same as E{0,m}.

E{n,m}

Matches at least n and at most m occurrences of E.

Capturing Text

括号允许我们将元素分组在一起,以便我们可以对它们进行量化和捕获。例如,如果我们有表达式mail|letter|correspondence,它匹配一个字符串,我们知道字符串与其中一个单词匹配,但不知道是哪个单词。使用括号可以“捕获”在其范围内匹配的任何内容,因此,如果我们使用(mail|letter|correspondence)并将这个regexp与字符串“I sent you some email”匹配,我们可以使用cap()或capturedtext()函数来提取匹配的字符,在本例中是“mail”。

我们可以在regexp本身中使用捕获的文本。要引用捕获的文本,我们使用从1索引的反向引用,这与cap()相同。例如,我们可以使用\b(\w+)\W+\1\b在一个字符串中搜索重复的单词,这意味着匹配一个单词边界,后面跟着一个或多个单词字符,后面再跟着一个或多个非单词字符,再跟着与第一个圆括号表达式相同的文本,后面再跟着一个单词边界。

如果我们想纯粹使用括号进行分组而不是捕获,我们可以使用非捕获语法,例如(?:green|blue)。非捕获括号以'(?:'开始,以 ')'结束。在这个例子中,我们匹配“绿色”或“蓝色”,但我们不捕捉匹配,所以我们只知道是否匹配,而不知道实际找到的颜色。使用非捕获括号比使用捕获括号更高效,因为regexp引擎需要做的簿记工作更少。

捕获和非捕获括号都可以嵌套。

由于历史原因,用于捕获括号的量词(例如*)比其他量词更“贪婪”。例如,a*(a*)将匹配“aaa”,cap(1) ==“aaa”。这种行为不同于其他regexp引擎所做的(尤其是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"]

Assertions(断言)

断言对出现在regexp中的文本做一些声明,但是它们不匹配任何字符。在下面的列表E代表任何表达式。

^

The caret signifies the beginning of the string. If you wish to match a literal ^ you must escape it by writing \\^. For example, ^#include will only match strings which begin with the characters '#include'. (When the caret is the first character of a character set it has a special meaning, see Sets of Characters.)

$

The dollar signifies the end of the string. For example \d\s*$ will match strings which end with a digit optionally followed by whitespace. If you wish to match a literal $ you must escape it by writing \\$.

\b

A word boundary. For example the regexp \bOK\b means match immediately after a word boundary (e.g. start of string or whitespace) the letter 'O' then the letter 'K' immediately before another word boundary (e.g. end of string or whitespace). But note that the assertion does not actually match any whitespace so if we write (\bOK\b) and we have a match it will only contain 'OK' even if the string is "It's OK now".

\B

A non-word boundary. This assertion is true wherever \b is false. For example if we searched for \Bon\B in "Left on" the match would fail (space and end of string aren't non-word boundaries), but it would match in "tonne".

(?=E)

Positive lookahead. This assertion is true if the expression matches at this point in the regexp. For example, const(?=\s+char) matches 'const' whenever it is followed by 'char', as in 'static const char *'. (Compare with const\s+char, which matches 'static const char *'.)

(?!E)

Negative lookahead. This assertion is true if the expression does not match at this point in the regexp. For example, const(?!\s+char) matches 'const' except when it is followed by 'char'.

通配符匹配

c

Any character represents itself apart from those mentioned below. Thus c matches the character c.

?

Matches any single character. It is the same as . in full regexps.

*

Matches zero or more of any characters. It is the same as .* in full regexps.

[...]

Sets of characters can be represented in square brackets, similar to full regexps. Within the character class, like outside, backslash has no special meaning.

在Wildcard(通配符)模式中,不能转义通配符。在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

Code Examples

  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)

The third string matches '6'. This is a simple validation regexp for integers in the range 0 to 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)

The second string matches 'This_is-OK'. We've used the character set abbreviation '\S' (non-whitespace) and the anchors to match strings which contain no whitespace.


In the following example we match strings containing 'mail' or 'letter' or 'correspondence' but only match whole words i.e. not '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

The second string matches "Please write the letter". The word 'letter' is also captured (because of the parentheses). We can see what text we've captured like this:

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

This will capture the text from the first set of capturing parentheses (counting capturing left parentheses from left to right). The parentheses are counted from 1 since cap(0) is the whole matched regexp (equivalent to '&' in most regexp engines).

这将从第一组捕获括号中捕获文本(计算从左到右捕获左括号的次数)。括号是从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"

Here we've passed the QRegExp to QString's replace() function to replace the matched text with new text.


  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()函数来重复匹配字符串中的regexp。注意,我们可以编写pos+ = rx.matchedLength()来跳过已经匹配的字符串,而不是一次向前移动一个字符。


regexp的一种常见用法是将带分隔符的数据行分割到它们的组件字段中。

  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);
  }

在本例中,我们的输入行具有公司名称、web地址和国家的格式。不幸的是,regexp相当长,而且不太通用——如果我们添加更多字段,代码就会崩溃。一个更简单、更好的解决方案是寻找分隔符,在本例中是'\t',并获取周围的文本。QString::split()可以将分隔符字符串或regexp作为参数,并相应地拆分字符串。

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

Here field[0] is the company, field[1] the web address and so on.


To imitate the matching of a shell we can use wildcard mode.

  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

Wildcard matching can be convenient because of its simplicity, but any wildcard regexp can be defined using full regexps, e.g. .*\.html$. Notice that we can't match both .html and .htm files with a wildcard unless we use *.htm* which will also match 'test.html.bak'. A full regexp gives us the precision we need, .*\.html?$.


QRegExp can match case insensitively using setCaseSensitivity(), and can use non-greedy matching, see setMinimal(). By default QRegExp uses full regexps but this can be changed with setPatternSyntax(). Searching can be done forward with indexIn() or backward with lastIndexIn(). Captured text can be accessed using capturedTexts() which returns a string list of all captured strings, or using cap() which returns the captured string for the given index. The pos() function takes a match index and returns the position in the string where the match was made (or -1 if there was no match).

QRegExp可以使用setCaseSensitivity()不敏感地匹配大小写,也可以使用非贪心匹配,参见setminima()。默认情况下,QRegExp使用完整的regexp,但这可以使用setPatternSyntax()进行更改。可以使用indexIn()向前搜索,也可以使用lastIndexIn()向后搜索。可以使用capturedtext()访问捕获的文本,capturedtext()返回所有捕获字符串的字符串列表,或者使用cap()返回给定索引的捕获字符串。pos()函数的作用是获取匹配索引并返回匹配字符串中的位置(如果没有匹配,则返回-1)。

相关博客:https://blog.csdn.net/wangrunmin/article/details/7377117

https://blog.csdn.net/qq_33266987/article/details/62045909

猜你喜欢

转载自blog.csdn.net/qq_25800311/article/details/84894653