也谈如何在C语言中巧用正则表达式,并且如何将其转化成C++的形式

C语言中巧用正则表达式

如果用户熟悉Linux下的sed、awk、grep或vi,那么对正则表达式这一概念肯定不会陌生。由于它可以极大地简化处理字符串时的复杂度,因此现在已经在许多Linux实用工具中得到了应用。千万不要以为正则表达式只是Perl、Python、Bash等脚本语言的专利,作为C语言程序员,用户同样可以在自己的程序中运用正则表达式。

  标准的C和C++都不支持正则表达式,但有一些函数库可以辅助C/C++程序员完成这一功能,其中最著名的当数Philip Hazel的Perl-Compatible Regular Expression库,许多Linux发行版本都带有这个函数库。 

编译正则表达式 

为了提高效率,在将一个字符串与正则表达式进行比较之前,首先要用regcomp()函数对它进行编译,将其转化为regex_t结构: 

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

参数regex是一个字符串,它代表将要被编译的正则表达式;参数preg指向一个声明为regex_t的数据结构,用来保存编译结果;参数cflags决定了正则表达式该如何被处理的细节。 

如果函数regcomp()执行成功,并且编译结果被正确填充到preg中后,函数将返回0,任何其它的返回结果都代表有某种错误产生。 

匹配正则表达式 

一旦用regcomp()函数成功地编译了正则表达式,接下来就可以调用regexec()函数完成模式匹配: 

 int regexec(const  regex_t  *preg,  const  char *string, size_t nmatch,regmatch_t pmatch[], int eflags);

typedef struct {   

             regoff_t rm_so;   

        regoff_t rm_eo; } regmatch_t;   参数preg指向编译后的正则表达式,参数string是将要进行匹配的字符串,而参数nmatch和pmatch则用于把匹配结果返回给调用程序,最后一个参数eflags决定了匹配的细节。 

在调用函数regexec()进行模式匹配的过程中,可能在字符串string中会有多处与给定的正则表达式相匹配,参数pmatch就是用来保存这些匹配位置的,而参数nmatch则告诉函数regexec()最多可以把多少个匹配结果填充到pmatch数组中。当regexec()函数成功返回时,从string+pmatch[0].rm_so到string+pmatch[0].rm_eo是第一个匹配的字符串,而从string+pmatch[1].rm_so到string+pmatch[1].rm_eo,则是第二个匹配的字符串,依此类推。 

 释放正则表达式 

无论什么时候,当不再需要已经编译过的正则表达式时,都应该调用函数regfree()将其释放,以免产生内存泄漏。 

                    void regfree(regex_t *preg);  

函数regfree()不会返回任何结果,它仅接收一个指向regex_t数据类型的指针,这是之前调用regcomp()函数所得到的编译结果。 

如果在程序中针对同一个regex_t结构调用了多次regcomp()函数,POSIX标准并没有规定是否每次都必须调用regfree()函数进行释放,但建议每次调用regcomp()函数对正则表达式进行编译后都调用一次regfree()函数,以尽早释放占用的存储空间。 

报告错误信息 

如果调用函数regcomp()或regexec()得到的是一个非0的返回值,则表明在对正则表达式的处理过程中出现了某种错误,此时可以通过调用函数regerror()得到详细的错误信息。 

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

参数errcode是来自函数regcomp()或regexec()的错误代码,而参数preg则是由函数regcomp()得到的编译结果,其目的是把格式化消息所必须的上下文提供给regerror()函数。在执行函数regerror()时,将按照参数errbuf_size指明的最大字节数,在errbuf缓冲区中填入格式化后的错误信息,同时返回错误信息的长度。 

注:以上来自http://www.chinaunix.net/jh/23/303346.html

                                                            用C++语言封装正则表达式

RegExp.hpp

#ifndef __HPP_REGEXP
#define __HPP_REGEXP
#include "autoconf/platform.h"
#include <sys/types.h>  // needed for size_t used in regex.h
#include <regex.h>
#include <string>
#include <deque>

#ifdef __GCCVER3
using namespace std;
#endif

class RegExp {

public:
    RegExp();
    ~RegExp();
    RegExp(const RegExp& r);
    bool comp(const char* exp);
    bool match(const char* text);
    int numberOfMatches();
    bool matched();
    std::string result(int i);
    unsigned int offset(int i);
    unsigned int length(int i);
    char* search(char* file, char* fileend, char* phrase, char* phraseend);

private:
    std::deque<std::string> results;
    std::deque<unsigned int> offsets;
    std::deque<unsigned int> lengths;
    bool imatched;
    regex_t reg;
    bool wascompiled;
    std::string searchstring;
};

#endif

RegExp.cpp

#include "RegExp.hpp"
#include <iostream>

RegExp::RegExp()
:imatched(false),wascompiled(false) {}

RegExp::~RegExp() {
    if (wascompiled) {
        regfree(&reg);
    }
}

RegExp::RegExp(const RegExp& r) {
    results.clear();
    offsets.clear();
    lengths.clear();
    unsigned int i;
    for(i = 0; i < r.results.size(); i++) {
        results.push_back(r.results[i]);
    }
    for(i = 0; i < r.offsets.size(); i++) {
        offsets.push_back(r.offsets[i]);
    }
    for(i = 0; i < r.lengths.size(); i++) {
        lengths.push_back(r.lengths[i]);
    }

    imatched = r.imatched;
    wascompiled = r.wascompiled;
    searchstring = r.searchstring;

   // 释放以前分配的内存,防止内存泄露
    if (wascompiled == true) {
        if (regcomp(&reg, searchstring.c_str(), REG_ICASE | REG_EXTENDED)) {
            regfree(&reg);
            imatched = false;
            wascompiled = false;
        }
    }
}

bool RegExp::comp(const char* exp) {

   if (wascompiled) {   // 释放内存 
        regfree(&reg);
        wascompiled = false;
    }
    results.clear();
    offsets.clear();
    lengths.clear();
    imatched = false;

   //  编译正则表达式失败
    if (regcomp(&reg, exp, REG_ICASE | REG_EXTENDED)) {  // compile regex
        regfree(&reg);

        return false;  // need exception?
    }
    wascompiled = true;
    searchstring = exp;
    return true;
}

bool RegExp::match(const char* text) {
      if (!wascompiled) {
        return false;  // need exception?
    }
    char* pos = (char*)text;
    int i;
    results.clear();
    offsets.clear();
    lengths.clear();
    imatched = false;
    regmatch_t *pmatch;
    pmatch = new regmatch_t[reg.re_nsub + 1];  // to hold result

    if (!pmatch) {  // if it failed
        delete[] pmatch;
        imatched = false;
        return false;
        // exception?
    }
    if (regexec(&reg, pos, reg.re_nsub + 1, pmatch, 0)) {  // run regex
        delete[] pmatch;
        imatched = false;
//        #ifdef DGDEBUG
//            std::cout << "no match for:" << searchstring << std::endl;
//        #endif
        return false;  // if no match
    }
    size_t matchlen;
    char* submatch;
    unsigned int largestoffset;
    int error = 0;
    while (error == 0) { // 字符串text中的匹配项相关信息依次插入到results、 offsets、engths中,便于以后操作
        largestoffset = 0;
        for (i = 0; i <= (signed)reg.re_nsub; i++) {
            if (pmatch[i].rm_so != -1) {
                matchlen = pmatch[i].rm_eo - pmatch[i].rm_so;
                submatch = new char[matchlen + 1];
                strncpy(submatch, pos + pmatch[i].rm_so, matchlen);
                submatch[matchlen] = '/0';
                results.push_back(std::string(submatch));
                offsets.push_back(pmatch[i].rm_so + (pos - text));
                lengths.push_back(matchlen);
                delete[] submatch;
                if ((pmatch[i].rm_so + matchlen) > largestoffset) {
                    largestoffset = pmatch[i].rm_so + matchlen;
                }
            }
        }

       if (largestoffset > 0) { // pmatch制定匹配数组太小,没有将text中的全部项匹配,从新从pos+ largestoffset开始进行匹配的操作。 
            pos += largestoffset;
            error = regexec(&reg, pos, reg.re_nsub + 1, pmatch, REG_NOTBOL);
        }
        else {
            error = -1;
        }

    }
    imatched = true;
    delete[] pmatch;
    #ifdef DGDEBUG
       std::cout << "match(s) for:" << searchstring << std::endl;
    #endif
    return true;  // match(s) found
}

 


std::string RegExp::result(int i) {
    if (i >= (signed)results.size() || i < 0) {  // reality check
        return ""; // maybe exception?
    }
    return results[i];
}

unsigned int RegExp::offset(int i) {
    if (i >= (signed)offsets.size() || i < 0) {  // reality check
        return 0; // maybe exception?
    }
    return offsets[i];
}

unsigned int RegExp::length(int i) {
    if (i >= (signed)lengths.size() || i < 0) {  // reality check
        return 0; // maybe exception?
    }
    return lengths[i];
}

int RegExp::numberOfMatches() {
    int i = (signed)results.size();
    return i;
}

bool RegExp::matched() {
    return imatched;  // regexp matches only
}


// My own version of STL::search() which seems to be 5-6 times faster
char* RegExp::search(char* file, char* fileend, char* phrase, char* phraseend) {

    int j, l;  // counters
    int p;  // to hold precalcuated value for speed
    bool match;  // flag
    int qsBc[256];  // Quick Search Boyer Moore shift table (256 alphabet)
    char* k;  // pointer used in matching

    int pl = phraseend - phrase;  // phrase length
    int fl = (int)(fileend - file) - pl;  // file length that could match

    if (fl < pl) return fileend;  // reality checking
    if (pl > 126) return fileend;  // reality checking

    // For speed we append the phrase to the end of the memory block so it
    // is always found, thus eliminating some checking.  This is possible as
    // we know an extra 127 bytes have been provided by NaughtyFilter.cpp
    // and also the OptionContainer does not allow phrase lengths greater
    // than 126 chars

    for(j = 0; j < pl; j++) {
        fileend[j] = phrase[j];
    }

    // Next we need to make the Quick Search Boyer Moore shift table

    p = pl + 1;
    for (j = 0; j < 256; j++) {  // Preprocessing
        qsBc[j] = p;
    }
    for (j = 0; j < pl; j++) {  // Preprocessing
        qsBc[(unsigned char)phrase[j]] = pl - j;
    }

    // Now do the searching!

    for(j = 0;;) {
        k = file + j;
        match = true;
        for (l = 0; l < pl; l++) {  // quiv, but faster, memcmp()
            if (k[l] != phrase[l]) {
                match = false;
                break;
            }
        }
        if (match) {
            return (j + file);  // match found at offset j (but could be the
                                // copy put at fileend)
        }
        j += qsBc[(unsigned char)file[j + pl]];  // shift
    }
    return fileend;  // should never get here as it should always match
}


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值