POSIX正則表達式


目錄

1       POSIX正則表達式

1.1     頭文件

#include <regex.h>

1.2     注意事項

     不過在C++的字符串中,'\'表示轉義后面的字符,'\\'才表示一個'\',而在正則中,'\'也表示轉義,所以一個'正常'的正則在修改為C++正則時,所有的'\'都要加倍。

     POSIX正則表達式有自己的一套規范,與常用的正則表達式不完全相同

1.3     基本方法

1.3.1     regcomp

函數原型

  int regcomp(regex_t *preg, const char *regex, int cflags);

功能

  編譯正則表達式,以便regexec方法使用。

數含義

preg

  preg是一個指向編譯后的正則表達式p思是pointerreg意思是regex_t類型。

  regex_t是一個結構體數據類型,用來存放編譯后的正則表達式,它的成員re_nsub用來存儲正則表達式中的子正則表達式的個數,子正則表達式就是用圓括號包起來的部分表達式。

regex

  描述正則表達式的字符串。

cflags

  決定編譯的類型。

    REG_EXTENDED

  擴展的正則表達式,如果不設置則為基本的正則表達式。

    REG_ICASE

  不區分大小寫。

   REG_NOSUB

  不用存儲匹配后的結果。

  如果設置該標志位,那么在regexec將忽略nmatchpmatch兩個參數。

   REG_NEWLINE

  識別換行符。

      ‘^’匹配行的開頭, 不管regexec中是否設置eflagsREG_NOTBOL

   ‘$’匹配行的末尾, 不管regexec中是否設置eflagsREG_NOTEOL

   例如:pattern=”^a$” 被檢測字符串為”\na”,那么設置REG_NEWLINE才能匹配成功。

返回值

        成功返回0

     失敗返回錯誤碼

1.3.2     regexec

函數原型

  int regexec(const regex_t *preg, const char *string, size_t nmatch,

                   regmatch_t pmatch[], int eflags);

功能

  根據編譯好的正則表達式,對字符串進行匹配。

參數含義

preg

  經過regcomp編譯的正則表達式。

string

  待匹配的字符串。

nmatch

  匹配到的字符串的個數。

pmatch

  匹配到的數組。

  regmatch_t 的定義如下

  typedef struct {

  regoff_t  rm_so;

  regoff_t  rm_eo;

  } regmatch_t;

  rm_so

如果-1==rm_so,表示沒有匹配到。

如果-1!=rm_so,表示string中下一個最大子字符串的偏移量(舉例string開頭的偏移量)。

  rm_eo

子字符串的長度。

eflags
   REG_NOTBOL

  NOTBOL = not-begin-of-line

  不匹配行的開頭,除非在regcomp編譯時cflag設置REG_NEWLINE

  該標志位可能在如下情況使用,傳遞string的不同部分給regexec,並且string的開頭不能被解釋成行的開頭。

  REG_NOTEOL

  NOTEOL = not-end-of-line

  不匹配行的結束,除非在regcomp編譯時cflag設置REG_NEWLINE

返回值

  成功返回0

  失敗返回REG_NOTMATCH

1.3.3     regerror

函數原型

  size_t regerror(int errcode, const regex_t *preg, char *errbuf,

                       size_t errbuf_size);

功能

  將regcompregexec返回的errorcode轉換成錯誤信息。

參數含義

errorcode

  錯誤碼,由regcompregexec獲得。

preg

  經過regcomp編譯的正則表達式。

errbuf

  存儲錯誤信息的buffer

errbuf_size

  errbuf的大小。

返回值

  返回errbuf存儲錯誤信息所需要的大小。

  如果errbuferrbuf_size都是非0,那么errbuf中有errbuf_size-1大小錯誤信息字符串,最后是字符串結束標記(‘\0’)

錯誤碼

regcomp方法返回的錯誤碼
   REG_BADBR

    BR=back reference

    非法使用返回引用操作符

   REG_BADPAT

    PAT=pattern

    非法使用正則表達式操作符,例如grouplist

   REG_BADRPT

    RPT=repetition

    非法使用重復的操作符,例如在第一個字符使用 ’*’

   REG_EBRACE

    EBRACE=error brace

    大括號不匹配

   REG_EBRACK

    EBRACK=error bracket

    中括號不匹配

   REG_ECOLLATE

    ECOLLATE=error collate

    非法參數

  REG_ECTYPE

    ECTYPE=error character type

    錯誤的字符類型

   REG_EEND

    EEND=error end

    無具體錯誤。該錯誤碼在POSIX.2中沒有定義

   REG_EESCAPE

    EESCAPE=error escape

    多余的\

  REG_EPAREN

    EPAREN=error parenthesis

    圓括號不匹配

  REG_ERANGE

    ERANGE=error range

    非法使用范圍操作符,例如結束的標識出現在開始標識之前

  REG_ESIZE

    ESIZE=error size

    編譯正則表達式需要的內存大於64kb,在POSIX.2中沒有定義

  REG_ESPACE

    ESPACE=error space

    編譯正則表達式已經超出內存空間

   REG_ESUBREG

    ESUBREG=error reference to a subexpression

    非法使用子表達式的引用

1.3.4     regefree

函數原型

  void regfree(regex_t *preg);

功能

  釋放由regcomp編譯preg的內存。

參數含義

preg

  經過regcomp編譯的正則表達式。

返回值

  void

1.4     POSIX正則表達式規范

參考:http://en.wikipedia.org/wiki/Regular_expression

  POSIX正則表達式分為Basic Regular Expressions Extended Regular Expressions

  ERE增加支持?,+|,去除了通配符(){}。而且POSIX正則表達式的標准語法經常堅持使用附加的語法來支持特殊應用。雖然POSIX.2沒有實現一些具體的細節,BREERE提供被很多工具使用的標准。

  BRE要求通配符(){}寫成\(\)\{\}ERE中無需這樣。

1.4.1     基本通配符

通配符

描述

.

匹配任何一個單字符(許多應用不包括換行符,但是假設包括換行符也是安全的)。在大中小括號中,該通配符只匹配字符’.’,例如,a.c匹配”abc”,但是[a.c]只匹配”a””.””c”。要匹配’.’不能使用’\.’,因為’\.’的作用與’.’一樣,應該使用[.]來匹配”a.c”中的點。

[]

只匹配中括號內的一個字符。例如[abc]匹配”a””b””c”[a-z]匹配小寫的”a””z”。這些格式可以混合使用:[abcx-z]匹配”a”,”b”,”c”,”x”,”y”,”z”[a-cx-z]的效果相同。

‘-’如果出現在[]的開頭或結尾,表示匹配字符’-’,例如[^-abc][-abc] [abc-]。注意不能使用’\’

‘]’可以出現在中括號中的第一個位置,例如[]abc][^]abc]

[^ ]

匹配單個字符,該字符不能包含在中括號中。例如,[^abc]匹配任何除’a’,’b’,’c’外的字符。[^a-z]匹配任何除’a’-‘z’的字符。同理,字符與范圍標識可以混合使用,例如,[^a-cx-z]

^

匹配字符串的開頭。在基於行的工具中,匹配每一行的開頭。

$

匹配字符串的結尾或者換行符的前一個位置。在基於行的工具中,匹配每一行的結尾。

()

定義一個子表達式。圓括號在整體匹配完后進行匹配。子表達式也叫做塊或組。

BRE模式需要寫成\(\)

\n

匹配第n(1<=n<=9)個子表達式。POSIX.2對該通配符的定義很模糊,有的工具允許引用大於9的子表達式。

*

匹配前一個元素0次或多次。例如,ab*c匹配”ac”,”abc”,”abbbbbc”等。[xyz]*匹配””,”x”,”y”,”z”,”zx”,”zyx”,”xyzzy”等。(ab)*匹配””,”ab”,”abab”等。

{m,n}

匹配前一個元素至少m次,至多n次。例如,a{3,5}匹配”aaa”,”aaaa”,”aaaaa”

BRE模式需要寫成\{m,n\}

 

舉例

.at

匹配任何以”at”結尾長度為3的字符串,例如” at”,”aat”,”cat”

[hc]at

匹配”hat””cat”

[^b]at

除了”bat”,匹配任何以”at”結尾長度為3的字符串。例如,”aat”,”cat”

[^hc]at

除了”hat””cat”,匹配任何以”at”結尾長度為3的字符串。例如,”aat”,”tat”

^[hc]at

匹配任何以”hat””cat”開頭的字符串或行

[hc]at$

匹配任何以”hat””cat”結尾的字符串或行

\[.\]

匹配任何三個字符,第一個和第三個字符必須分別為”[””]”,因為”\[””\[”是經過轉義,例如”[a]”,”[b]”

s.*

匹配任何以”s”開頭的字符串,例如”swa”,”seed”

1.4.2     Extended Regular Expressions

ERE中,反斜杠’\’用來對通配符進行轉義,所以BRE中的’\(’’\)’ERE中改為’(’’)’’\{’’\}’改為’{’’}’ ERE移除了’\n’通配符,並添加了如下通配符。

通配符

描述

?

匹配前一個元素0次或1次。例如,ab?c匹配”ac”,”abc”

+

匹配前一個元素1次或多次。例如,ab+c匹配”abc”,”abbc”等,但是不能匹配”ac”

|

匹配前一個表達式或后一個表達式。例如,abc|def匹配”abc”,”def”

 

[hc]+at

匹配”hat”,”cat”,”hhat”,”ccat”等,但是不匹配”at”

[hc]?at

匹配”hat”,”cat”,”at”

[hc]*at

匹配”at”,”hat”,”cat”,”hcat”

cat|dog

匹配”cat”,”dog”

1.4.3     Character classes

character class 是除了字面匹配最基本的正則表達式。它是很小的字符序列匹配更大的字符序列。例如,[A-Z]可以表示字母表,\d表示任意數字。character class應用於BREERE

當使用范圍通配符時,例如[a-z]。計算機本地設置決定了字符編碼的順序。計算機可能按a-z的順序來存儲,或者abc…zABC…Z,或者aAbBcC…zZ的順序。所以POSIX定義了character class,正則表達式的處理器可以正確解析該character class

POSIX

ASCII

描述

[:alnum:]

[A-Za-z0-9]

數字和字母字符

[:alpha:]

[A-Za-z]

字母字符

[:blank:]

[ \t]

空格和TAB

[:cntrl:]

[\x00-\x1F\x7F]

控制符

[:digit:]

[0-9]

數字

[:graph:]

[\x21-\x7E]

可視字符

[:lower:]

[a-z]

小寫字母字符

[:print:]

[\x20-\x7E]

可視字符和空格

[:punct:]

[][!"#$%&'()*+,./:;<=>?@\^_`{|}~-]

標點符號

[:space:]

[ \t\r\n\v\f]

空白字符

[:upper:]

[A-Z]

大寫字母字符

[:xdigit:]

[A-Fa-f0-9]

十六進制字符

POSIX定義的character class只能在中括號內使用。例如,[[:upper:]ab]匹配大寫字母字符和”a”,”b”

[:word:]是附加的非POSIXcharacter class[:word:]表示[:alnum:]和下划線。這表明在很多編程語言中,這些通配符可能是標識符。

1.5     源碼示例

我的githubhttps://github.com/loverszhaokai/libutil/blob/master/src/regex_util.cc

libutil庫中包含有regex的示例。歡迎批評指正,一起修改。

 

/******************************************************************************
libutil
Author: zhaokai
Email: loverszhao@gmail.com

Reference: POSIX

Description:

Version: 1.0

******************************************************************************/

#include "util/regex_util.h"

#include <algorithm>
#include <climits>
#include <iostream>
#include <regex.h>

namespace util
{

bool
RegexMatch(const std::string &_pattern,
           const std::string &_input,
           std::vector<std::string> *_output,
           std::string *_err_msg)
{
  _output->clear();
  *_err_msg = "";
  regex_t reg;

  int comp_result = regcomp(&reg, _pattern.c_str(), REG_EXTENDED | REG_NEWLINE);
  if (0 == comp_result)
  {
    // Add 1 in case of "" == _input
    size_t nmatch = 1 + std::min(_input.length(), UINT_MAX - 1);
    regmatch_t pmatch[nmatch];
    int exec_result = regexec(&reg, _input.c_str(), nmatch, pmatch, 0);

    if (0 == exec_result)
    {
      for (size_t i = 0; i < nmatch; ++i)
      {
        if (-1 == pmatch[i].rm_so)
          break;

        _output->push_back(_input.substr(pmatch[i].rm_so, pmatch[i].rm_eo));
      }
    }
  }
  else
  {
    const size_t errbuf_size = 1000;
    char errbuf[errbuf_size + 1];
    regerror(comp_result, &reg, errbuf, errbuf_size);
    *_err_msg = errbuf;
    
    regfree(&reg);
    return false;
  }

  regfree(&reg);
  return true;
}

bool
FullMatch(const std::string &_pattern,
          const std::string &_input)
{
  std::vector<std::string> output;
  std::string err_msg = "";
  bool result = RegexMatch(_pattern, _input, &output, &err_msg);

  if (!result)
  {
    std::cout << err_msg << std::endl;
    return false;
  }
  else
  {
    // full-match in case of @_input == output[0]
    if (0 < output.size() && _input == output[0])
      return true;
  }
  return false;
}

}; // namespace util

 

單元測試文件,包含了Mac,IP,Port,文件夾路徑的正則表達式測試

 

/******************************************************************************
libutil
Author: zhaokai
Email: loverszhao@gmail.com

Reference: chromium

Description:

Version: 1.0

******************************************************************************/

#include "util/regex_util.h"

#include "util/basictypes.h"

#include "third_party/gtest/include/gtest/gtest.h"

namespace util
{
// ------------------------------------------------------------
// --------------------- Update Begin -------------------------
// ------------------------------------------------------------

TEST(RegexUtilTest, RegexMatch)
{
  struct
  {
    const std::string pattern;
    const std::string input;
    const std::vector<std::string> expect_output;
    bool result;
  } cases [] = {
    { "c", "c", {"c"}, true},
    { "c", "", {}, true},
    { "(", "", {}, false},
    { "0*", "", {""}, true},
    { "0*", "0", {"0"}, true},
    { "0*", "0000", {"0000"}, true},
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i)
  {
    std::string err_msg = "";
    std::vector<std::string> output;
    EXPECT_EQ(cases[i].result, RegexMatch(cases[i].pattern, cases[i].input, &output, &err_msg))
        << "cases[" << i << "]";
    EXPECT_EQ(cases[i].expect_output, output)
        << "cases[" << i << "]";
  }
}

TEST(RegexUtilTest, FullMatch)
{
  struct
  {
    const std::string pattern;
    const std::string input;
    bool result;
  } cases [] = {
    { "c", "c", true},
    { "c", "", false},
    { "(", "", false},
    { "^[0-9]*$", "12345", true},
    { "^[0-9]*$", "00000", true},
    { "^[0-9]*$", "1", true},
    { "\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*", "loverszhao@gmail.com", true},
    { "[0-9]{5}", "00000", true},
    { "([0-9]){5}", "00000", true},
    { "(([0-9]){5}){2}", "0000000000", true},
    { "(([0-9]){5}){2}", "0000011111", true},
    { "(([0-9]){5}){2}", "0123456789", true},
    { "[0-9]", "0", true},
    { "0*", "00000", true},
    { "0*", "", true},
    { "0?", "", true},
    { "0?", "0", true},
    { "0?", "00", false},
    { "[a-z][0-9]{2}{3}", "x000000", true},
    { "^a$", "a", true},
    { "^a$", "\na", false},
    { ".", "a", true},
    { ".", "abc", false},
    { "\.", ".", true},
    { "[.]", "a", false},
    { "[:digit:]", "1", false},
    { "[[:digit:]]", "1", true},
    { "[[:digit:]]{1,2}", "1", true},
    { "[[:digit:]]{1,2}", "99", true},
    { "^[[:digit:]]{1,2}", "1", true},
    { "^[[:digit:]]{1,2}", "99", true},
    //"^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])\.";
    { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]])", "99", true},
    { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]])", "199", true},
    { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])", "99", true},
    { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])", "199", true},
    { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])", "239", true},
    { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])", "250", true},
    { "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])\.", "250.", true},
    { "^([\\/]?[[:alnum:]_]+)*$", "file_name", true},

    
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i)
  {
    EXPECT_EQ(cases[i].result, FullMatch(cases[i].pattern, cases[i].input))
        << "cases[" << i << "] pattern=" << cases[i].pattern
        << " input=" << cases[i].input << std::endl;
  }
}


namespace
{

// Return true if @_mac is valid, such as "11:11:11:11:11:11"
// or "11 11 11 11 11 11" or "111111111111"
// flase otherwise
bool
ValidateMac(const std::string &_mac)
{
  const std::string pattern = "^[0-9a-fA-F]{2}([ -:][0-9a-fA-F]{2}){5}";
  return FullMatch(pattern, _mac);
}

  
// Return true if @_ip is valid, such as "192.168.4.1"
// flase otherwise
bool
ValidateIP(const std::string &_ip)
{
  const std::string pattern =
      "^([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])[.]"
      "([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])[.]"
      "([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])[.]"
      "([[:digit:]]{1,2}|1[[:digit:]][[:digit:]]|2[0-4][[:digit:]]|25[0-5])$";

  return FullMatch(pattern, _ip);
}

// Return true if @_port is valid which should between "0~65535"
// and it can be "0" or "65535"
// false otherwise
bool
ValidatePort(const std::string &_port)
{
  const std::string pattern = "^([1-9]|[1-9][[:digit:]]{1,3}|[1-6][0-5][0-5][0-3][0-5])$";
  return FullMatch(pattern, _port);
}

// Return true if @_path is valid, such as "/1/2/3" or "1/2/3/"
// false otherwise
bool
ValidatePath(const std::string &_path)
{
  //const std::string pattern = "^[a-zA-Z]:(((//(?! )[^///:*?\"<>|]+)+//?)|(//))[:space:]*$";
  //const std::string pattern = "(^[.]|^/|^[a-zA-Z])?:?/[.]+(/$)? ";
  //const std::string pattern = "(\/([0-9a-zA-Z]+))+";
  const std::string pattern = "^([\\/]?[[:alnum:]_]+)*$";
  return FullMatch(pattern, _path);
}

TEST(RegexUtilTest, ValidateMac)
{
  struct
  {
    const std::string input;
    bool result;
  } cases [] = {
    { ":11:22:33:44:55:66", false},
    { "11:22:33:44:55:66", true},
    { "11-22-33-44-55-66", true},
    { "11 22 33 44 55 66", true},
    { "112233445566", false},
    { "c", false},
    { "c", false},
    { "(", false},
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i)
  {
    EXPECT_EQ(cases[i].result, ValidateMac(cases[i].input))
        << "cases[" << i << "] input=" << cases[i].input << std::endl;
  }
}

TEST(RegexUtilTest, ValidateIP)
{
  struct
  {
    const std::string input;
    bool result;
  } cases [] = {
    // Normal IP
    { "192.168.4.1", true},
    { "10.0.0.1", true},
    { "192.168.4.244", true},
    // Unnormal IP
    { "292.168.4.244", false},
    { "192.268.-1.244", false},
    { "192.168.4.264", false},
    { "....", false},
    { "192.168.1.", false},
    { "192 168 1 1", false},
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i)
  {
    EXPECT_EQ(cases[i].result, ValidateIP(cases[i].input))
        << "cases[" << i << "] input=" << cases[i].input << std::endl;
  }
}

TEST(RegexUtilTest, ValidatePort)
{
  struct
  {
    const std::string input;
    bool result;
  } cases [] = {
    // Normal Port
    { "1", true},
    { "99", true},
    { "199", true},
    { "200", true},
    { "999", true},
    { "1999", true},
    { "30000", true},
    { "30001", true},
    { "60000", true},
    { "65535", true},

    // Unnormal Port
    { "0", false},
    { "-1", false},
    { "00", false},
    { "010", false},
    { "65536", false},
    { "65537", false},
    { "111111", false},
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i)
  {
    EXPECT_EQ(cases[i].result, ValidatePort(cases[i].input))
        << "cases[" << i << "] input=" << cases[i].input << std::endl;
  }
}

TEST(RegexUtilTest, ValidatePath)
{
  struct
  {
    const std::string input;
    bool result;
  } cases [] = {
    // Normal Path
    { "1", true},
    { "/1/2/3", true},
    { "1/2/3", true},

    // Unnormal Path
    { ".", false},
    { "..", false},
    { "/1/./", false},
    { "/1/../", false},
    { "//1/2", false},
    { "/1/...", false},
    { "/1//", false},
    { "./1/./", false},
    { "./1/../", false},
    { "./1/2/3", false},
    { "./1/2/3/", false},
    { "1/./", false},
    { "1/../", false},
  };

  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i)
  {
    EXPECT_EQ(cases[i].result, ValidatePath(cases[i].input))
        << "cases[" << i << "] input=" << cases[i].input << std::endl;
  }
}



}


}; // namespace util

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM