也談如何在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
}


 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章