因為作業的要求,我需要識別用戶從命令行輸入的多項式,並且要提取出其中的系數、指數以便用於后續計算。
曾經想過用一個數組把用戶所有的輸入全部存進來,然后在寫邏輯判斷。但想想那復雜的邏輯,頭皮都發麻,這時候因為有個朋友拜托我寫個簡單的小爬蟲,寫完后我靈機一動,爬蟲用到的正則表達式不就是最好的處理手段么??(感謝同學,大霧)
言歸正傳。
首先先簡單的講一講關於正則表達式的知識,這里就只關注那些我一會兒會用到的。
[ ] 方括號在這里被叫做原子表,它能夠匹配括號中出現的任意一個元素
[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;