用正則表達式(regex)匹配多項式(polynomial)


因為作業的要求,我需要識別用戶從命令行輸入的多項式,並且要提取出其中的系數、指數以便用於后續計算。

曾經想過用一個數組把用戶所有的輸入全部存進來,然后在寫邏輯判斷。但想想那復雜的邏輯,頭皮都發麻,這時候因為有個朋友拜托我寫個簡單的小爬蟲,寫完后我靈機一動,爬蟲用到的正則表達式不就是最好的處理手段么??(感謝同學,大霧)


 

言歸正傳。

首先先簡單的講一講關於正則表達式的知識,這里就只關注那些我一會兒會用到的。

[ ] 方括號在這里被叫做原子表,它能夠匹配括號中出現的任意一個元素

 

[abcd] //匹配abcd中任意一個字母
[!@#] // 匹配!@#中任意一個符號
[0-9] //匹配0,1,2,3,4,5,6,7,8,9中任意一個數字

三個常用的特殊符號: * + ?

    • * 星號可以匹配前面出現的元素0次,1次或任意多次
    • + 加號可以匹配前面出現的元素1次或任意多次
    • ? 問號可以匹配前面出現的元素0次或1次
ab* //匹配 a,ab,abb,abbb.......
ab+ //匹配 ab,abb,abbb,abbbb.......
ab? //匹配 a,ab

( ) 圓括號在這里被叫做模式單元符,他能夠將一些元素組合成一個大的,不可分割的新元素,類似於括號在正常運算中的作用。

(ab*)? //匹配 空串 或 a,ab,abb,abbb.......
(ab+)? //匹配 空串 或 ab,abb,abbb,abbbb.......
(ab?)? //匹配 空串 或 a,ab

匹配任意字符:.(除換行符)

a.c  //匹配aac,abc,acc,adc.......

   轉義符 : 如果想要匹配本身擁有特殊意義的符號,可以使用轉移符\

\. // 匹配一個小數點

邊界限制字符:^ $

  • ^ 匹配字符串的開始
  • $ 匹配字符串的結束
^a.* //匹配以a開頭的字符串
.*a$ //匹配以a結尾的字符串

 


 

好了,現在可以回過頭來看看我們要處理的問題了。

我們要處理的是類似這樣的,由用戶輸入的字符串,用戶很可能會輸入一些空格,所以最穩妥的辦法是用一個字符串存下一整行的輸入信息,然后從中刪去所有的空格和制表符。

x^2+x-3x^-1

如何匹配這樣復雜的表達式呢??首先還是得拆分,一點點入手。

我們可以把一個完整的多項式拆成若干項

x^2
+x
-3x^-1

先看看如何匹配其中的一項吧

很明顯,最先出現的是表示正負的符號,但也可以不出現

[+-]? //匹配加號,減號,或不出現符號

 然后就應該考慮匹配一個浮點數了,當然,如果系數為1,這個浮點數也可以不出現 所以最后可以用一個?表達可以不出現的意思。

[0-9]*\.?[0-9]+ //匹配一個浮點數
([0-9]*\.?[0-9]+)? //這個浮點數也可以不出現

 

然后考慮匹配指數位,當然,在指數為1的時候,也可以不出現,指數為0的時候,x也不用出現

\\^[+-]?[0-9]+ //匹配^1,^2,^3......
x(\\^[+-]?[0-9]+)? //匹配x^1,x^2,x^3...... 或者x (指數為1的情況)
(x(\\^[+-]?[0-9]+)?)? //允許匹配空串,用於指數為0的情況

 

好了,現在可以講上述三個部分組合在一起了

[-+]?([0-9]*\.?[0-9]+)?(x(\\^[+-]?[0-9]+)?)? // 匹配 x^2,-3x^5,1,-x^-1......

 

還剩最后一點點就能完成了!!下面就是讓這整個部分多次出現,用於匹配整個多項式,同時通過^匹配字符串的開頭

^([-+]?([0-9]*\.?[0-9]+)?(x(\\^[+-]?[0-9]+)?)?)+$ //匹配整個多項式

 

emmmmm,好!如果你沒有強迫症的話,大功告成了!

 

如果你有呢?

emmmmm,那就得再想想了。

在一個多項式中,符號,系數,指數不是必須全部出現的,但也不能全都不出現。反正一共只有23鍾可能性,我們一樣考慮一下就好了。(下面表格中星號代表這項出現)

符號 系數 指數 示例 是否可行 正則表達式
* * * -2x^2
[-+]([0-9]*\.?[0-9]+)(x(\\^[+-]?[0-9]+)?)
  * * 2x^2
([0-9]*\.?[0-9]+)(x(\\^[+-]?[0-9]+)?)
*   * -x^2
[-+](x(\\^[+-]?[0-9]+)?)
* *   -2
[-+]([0-9]*\.?[0-9]+)
*        
  *   3 ([0-9]*\.?[0-9]+)
    * x (x(\\^[+-]?[0-9]+)?)
         

發現有六種可行的情況,那就用笨辦法依次匹配這六種情況唄。

然后把這六種打上括號,用 | 連接起來,就構成了修訂后的用於匹配某一項的正則表達式。

([-+]([0-9]*\.?[0-9]+)(x(\\^[+-]?[0-9]+)?))|(([0-9]*\.?[0-9]+)(x(\\^[+-]?[0-9]+)?))|([-+](x(\\^[+-]?[0-9]+)?))|([-+]([0-9]*\.?[0-9]+))|(([0-9]*\.?[0-9]+))|((x(\\^[+-]?[0-9]+)?))

 

然后再像剛才一樣 ,讓這整個部分多次出現,用於匹配整個多項式,同時通過^匹配字符串的開頭

^(([-+]([0-9]*\.?[0-9]+)(x(\\^[+-]?[0-9]+)?))|(([0-9]*\.?[0-9]+)(x(\\^[+-]?[0-9]+)?))|([-+](x(\\^[+-]?[0-9]+)?))|([-+]([0-9]*\.?[0-9]+))|(([0-9]*\.?[0-9]+))|((x(\\^[+-]?[0-9]+)?)))+$

 

接下來把代碼實現方式擺出來,哦對了,要使用正則表達式的話,記得 #include <regex>

std::string poly = "x^2-x+3+x^-5";

std::string p1 = std::string("(") + "[-+]([0-9]*\.?[0-9]+)(x(\\^[+-]?[0-9])?)" + ")";

std::string p2 = std::string("(") + "([0-9]*\.?[0-9]+)(x(\\^[+-]?[0-9])?)" + ")";
std::string p3 = std::string("(") + "[-+](x(\\^[+-]?[0-9])?)" + ")";
std::string p4 = std::string("(") + "[-+]([0-9]*\.?[0-9]+)" + ")";
std::string p5 = std::string("(") + "([0-9]*\.?[0-9]+)" + ")";
std::string p6 = std::string("(") + "(x(\\^[+-]?[0-9])?)" + ")";
std::string p = p1 + "|" + p2 + "|" + p3 + "|" + p4 + "|" + p5 + "|" + p6;
std::string p_ = std::string("^(") + p + ")+$";
std::regex polyPattern(
p_);
if(!std::regex_match(poly,polyPattern)){
  std::cout
<<"invalid polynomial!"<<std::endl;
}

下面我要做的,就是把每一項中的系數和指數識別出來,然后分別轉換成double和int類型的變量便於計算。

std::regex cofPattern("^[-+]?([0-9]*\.?[0-9]+)"); //匹配系數

 

 因為不能匹配 x, -x 這兩種情況,所以在寫代碼的時候可以在適當判斷一下

std::smatch cofResult;
double cof;
std::string term = "3x^5";
if( std::regex_search(term,cofResult,cofPattern) ){ const char* tempCof = cofResult.str().data(); //atof函數的參數是 const char* 類型的,我們需要轉換一下 cof = std::atof(tempCof); // 將字符串轉換成double類型數字 }else if(term[0] == '-') cof = -1; else cof = 1;

 

 下面是最后一步!提取指數

std::regex expPattern1("x\\^[+-]?[0-9]+$"); // 匹配指數

 但由於這樣無法匹配指數為1的情況,所以我又寫了個單獨的正則表達式

std::regex expPattern2("x$"); // 匹配x在末尾
std::smatch expResult;
int exp;
if( std::regex_search(term,expResult,expPattern1) ){ const char* tempExp = expResult.str().substr(2).data(); exp = std::atoi(tempExp); }else if(std::regex_search(term,expResult,expPattern2)) exp = 1; else exp = 0;

 


免責聲明!

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



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