C# 正規表示式判斷是否是有效的檔案、資料夾路徑
省流
/// <summary>
/// 是否有效的檔案,資料夾路徑
/// </summary>
/// <param name="val"></param>
/// <returns>是,返回true;不是返回false</returns>
public bool IsValidFolderPath(string val)
{
Regex regex = new Regex(@"^([a-zA-Z]:\\)([-\u4e00-\u9fa5\w\s.()~!@#$%^&()\[\]{}+=]+\\?)*$");
Match result = regex.Match(val);
return result.Success;
}
// "F:\\Total客戶端專案\\客戶端專案\\2017-01-09 Client\\(aa)\\V1.3.4\\New_1.2\\V13&V14\\.()~!@#$%^&()-+="; // 匹配結果:true
解釋:
分為2大段,一段匹配碟符,一段匹配後續檔案、資料夾路徑
-
^
([a-zA-Z]:\\)
:必須以碟符的形式開頭。^
表示從起始位置匹配,[a-zA-Z]
表示第1位必須是a~z
或A~Z
其中之一。:\\
表示第1位後必須接字串:\
。\\
是正則中\
的轉義。 -
([-\u4e00-\u9fa5\w\s.()~!@#$%^&()\[\]{}+=]+\\?)*$
:後續以一定取值範圍組成一個個結構。先看[]內,
\u4e00-\u9fa5
表示匹配漢字,\w
,\s
都是元字元有其對應的匹配範圍。其餘這些字元-.()~!@#$%^&()\[\]{}+=
就代表它們自身。其中\[
是[
的轉義,\]
是]
的轉義。[~]+
表示[]
中的內容至少需要出現1次。\\?
表示,[~]
內的字元寫完後,可以在後面接一個字元\
,也可以不接。(~)*
表示()
內容可以重複任意次數,也可以一次不出現。$
表示匹配到結束位置,搭配前面的^
表示整個輸入字串的結構都得符合這個正規表示式。
幾點注意:
- 上面得
[~]
指代表示式中[]
的所有內容,(~)
指代表示式中()
的所有內容,應該挺好理解吧。 - 寫了解釋主要是自己總結一下,你看估計也看不懂,要不直接拿去用,要不老老實實去學吧,這些都是基礎,就把正規表示式的基礎學了基本就夠用。
- 不同系統下可不可以匹配漢字是不一樣的。比如C#環境裡
\w
好像就可以匹配漢字,但javascript環境裡\w
就匹配不了漢字。 - 正則自己的轉義和放入字串中的轉義挺容易懵的,寫的時候要注意。
- 個人理解,只有一個路徑,是判斷不出來這個路徑是檔案還是資料夾的,因為資料夾名也可以叫
setup.exe
,檔名也可以沒有字尾。windows的檔案命名規範中只不允許9個字元的出現。/ \ ? * : " < > |
其他都可以。
學習編寫驗證過程
鑑於網上找了好幾個都是垃圾,既不好使也不知道到底在判斷啥,所以不得不萬事靠自己。
學習自https://deerchao.cn/tutorials/regex/regex.htm
元字元metacharacter
字元 | 相關解釋 | |
---|---|---|
\b | 匹配單詞的開頭或結尾,也就是單詞的分界處。可用於精確查詢一個單詞 | |
. | 匹配除了換行以外的任意字元 | |
* | *前面的內容可以重複任意次 | |
+ | +前面的內容可以連續重複1或任意更多次。通俗一點說,就是至少得匹配一次。 | |
? | ?前面的內容可以連續重複0或1次。 | |
{x} | x:數字。{x}前面的內容必須重複x次 | |
{x,} | x。{x,}前面的內容必須重複至少x次 | |
{x,y} | x,y:數字。{x,y}前面的內容必須重複x,y之間的次數,包括x,y | |
(xxx) | 表示分組 | |
[x,y,z] | 表示單個匹配 | |
\d | 匹配一位十進位制數字,也就是0~9 | [0-9] |
\s | 匹配任意空白符,空格,製表符,換行符,中文全形空格等 | |
\w | 匹配數字,字母,下劃線【中文】 | [a-z0-9A-Z_] |
^ | 匹配字串的開始位置 | |
$ | 匹配字串的結束位置 |
如何從一個字串中查詢字串‘hi’?
Regex regex = new Regex("hi");
// 注意:如history,high等詞中的hi也會被匹配。
如何精確查詢hi這個詞?使用\b
Regex regex = new Regex(@"\bhi\b");
這樣就可以精確查詢到‘hi’這個詞。
如何查詢hi,xxxxx,Lucy?
Regex regex = new Regex(@"\bhi\b.*\bLucy\b");
// `.*`不能換行。是匹配不包含換行的任意數量字元
如何匹配一箇中國電話號碼?格式為:xx-xxxxxxxxxxxxxxx
Regex regex = new Regex(@"\d\d-\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d");
如何匹配一個188開頭的電話號碼?
Regex regex = new Regex(@"\d\d-188\d\d\d\d\d\d\d\d\d\d\d\d");
這如果要匹配一個100位數字豈不是都寫不下了,所以必然存在優化寫法。
Regex regex = new Regex(@"\d{2}-188\d{12}");
\d{2} 的話能不能匹配85555這種?與high中匹配hi一樣?
如果沒有限制也是會都匹配的,如果想要精確匹配,也要在前後使用元字元\b
如果有長號有短號想一起匹配怎麼辦?
Regex regex = new Regex(@"\d{2,3}-188\d{6,12}");
解析一個電話號相關的表示式^\(?0\d{2}[) -]?\d{8}
^
:表示驗證字串必須以(
或0開頭\(?
:\(
是(
的轉義,?
表示出現1次或不出現0\d{2}
: 表示0起始後面跟2位數字[) -]?
:表示數字後面的1位或者什麼都沒有,或者是)
,空格
,-
3箇中的一個\d{8}
:表示8位數字。
string phone = "(010)88886666";
string phone1 = "(010x88886666";
string phone2 = "011)-888866660000";
string phone3 = "011-888866660000";
string phone4 = "99011-88886666";
Regex regex30 = new Regex(@"^\(?0\d{2}[) -]?\d{8}");
Match resultphone = regex30.Match(phone);// 匹配成功
Match resultphone1 = regex30.Match(phone1);// 匹配失敗,因為010後出現了[]中不存在的字元
Match resultphone2 = regex30.Match(phone2);// 匹配失敗,因為[]後沒有接8位數字,不是說-也在[]中就可以,[]永遠只匹配一個位置
Match resultphone3 = regex30.Match(phone3);// 匹配成功,因為這個正規表示式沒有用$限制結尾
Match resultphone4 = regex30.Match(phone4);// 匹配失敗,因為這個正規表示式用^限制了開頭,必須為(或0。
關於^
和$
以前老不理解這玩意都啥用,其實就是對匹配範圍進行限制。以一段數字字元為例。
正常使用9/d{2}
進行匹配,匹配條件可以解釋為,“以9開頭並在後面跟任意2位數字的字串”可以匹配成功,也就是998。
但如果使用^進行限制^9/d{2}
。匹配條件就變為“輸入字串必須是以9並在後面跟任意2位數字開頭的字串”。匹配就會失敗。
把9去掉^/d{2}
,匹配條件就變為“輸入字串必須是以任意2位數字開頭的字串”。匹配就會成功。
若用$限制/d{2}$
,匹配條件就變為“輸入字串必須是以任意2位數字結尾的字串”。匹配成功。
若改為58$
,匹配條件就變為“輸入字串必須是以‘58’結尾的字串”。匹配就會失敗。
若用^,$
限制,^/d{2}$
,匹配條件就變為“輸入字串必須是2位數字的字串”,匹配失敗。
改為^/d{18}$
,,匹配條件就變為“輸入字串必須是18位數字的字串”,匹配成功。
程式碼如下:
string numberStr2 = "123456789987645312";
Regex regex2 = new Regex(@"9\d{2}");
Match result2 = regex2.Match(numberStr2);
Regex regex3 = new Regex(@"^9\d{2}");
Match result3 = regex3.Match(numberStr2);
Regex regex4 = new Regex(@"\d{2}$");
Match result4 = regex4.Match(numberStr2);
Regex regex5 = new Regex(@"58$");
Match result5 = regex5.Match(numberStr2);
Regex regex6 = new Regex(@"^\d{2}$");
Match result6 = regex6.Match(numberStr2);
Regex regex7 = new Regex(@"^\d{18}$");
Match result7 = regex7.Match(numberStr2);
關於()
,[]
,和{}
首先是{},這個沒什麼說的,就是表示重複次數的。{2},{2,},{2,5}這種。
其次[]表示單個匹配。只能表示1個位置,這個位置的內容必須為[]中的選項之一。
看到這麼描述大約有以下幾種疑問
單獨使用[]有啥用?
Regex regex8 = new Regex(@"[打]");
Match result8 = regex8.Match(Str8);
Regex regex9 = new Regex(@"[打s]");
Match result9 = regex9.Match(Str8);
Regex regex10 = new Regex(@"[打s黑]");
Match result10 = regex10.Match(Str8);
Regex regex11 = new Regex(@"[黑]");
Match result11 = regex11.Match(Str8);
Regex regex12 = new Regex(@"打");
Match result12 = regex12.Match(Str8);
Regex regex13 = new Regex(@"黑s打");
Match result13 = regex13.Match(Str8);// 匹配失敗
Regex regex14 = new Regex(@"[黑s打]");// 匹配成功,找到‘打’
Match result14 = regex14.Match(Str8);
// 單獨使用就是從頭至尾匹配輸入字串的每一個字元。找到輸入字串中第一個能與[]中任意一個字元匹配的上的。
// 如果[]中只有1個字元,那有沒有[]完全一樣。如果[]內有多個字元是不一樣的。
// 想象不出來使用場景,[]在實際應用中也大多配合其他條件一起使用
[]裡要是有元字元怎麼辦?
解決方法很簡單:轉義。
但具體怎麼轉其實還是有點繞。這個繞不是說有多難,而是這個點你需要有印象,遇到的時候要能反映過來。
這個我認為很容易懵的點在於C#自身字串的轉義與正規表示式的轉義混合。
首先明確一下C#中的轉義,C#中轉義有2種方法:
// 字元原文:
//a我打[]{}aa\bb"cc''dd^ee/dff
//another row
// '[',']','{','}',''','^','/'本身就不需要轉義,需要轉義的是'\','"',換行
// 第1種 需要轉義的符號前加'\'
string stringStr1 = "a我打[]{}aa\\bb\"cc''dd^ee/dff\r\nanother row";
// 第2種,整個字串用'@'修飾
// 這種情況下,'\',換行不用轉義了。但'"'還需要轉義,因為不轉義字串就提前結束了,用兩個雙引號'""'表示普通字元'"'
string stringStr2 = @"a我打[]{}aa\bb""cc''dd^ee/dff
another row";
正規表示式中需要轉義的符號很多,所有元字元全部需要轉義。但好訊息是轉義方式只有1種,就是在需要轉義的符號前加\
。
這再把這些表示式放入C#字串中,就分不清到底是字串轉義,還是正則轉義,正則轉義後進入字串會不會又要轉義等等等等。懵。
元字元包括:( ) [ ] { } \ ^ $ | ? * . + /。 ”/“需不需要轉義好像有點爭議。查了一下發現很多編譯器關於正則的轉義是有一些預設處理的,也沒找到個權威的規則,就視具體情況而定。
// 上面的例子,匹配一個9開頭跟2個數字的字元
Regex regex15 = new Regex("9\d{2}"); // 這樣寫就會報錯,因為正則'\d'中的'\'是c#字串中轉義的標識。這麼寫C#就會認為'\d'是一個轉義符,而又不知道轉義成了什麼,就會報錯:CS1009:無法識別的轉義序列。
// 所需要將'\'轉義,如上使用@或另一種轉義方法都可以
Regex regex16 = new Regex(@"9\d{2}");
Regex regex17 = new Regex("9\\d{2}");
// 另一個很需要有印象的例子。就是一個位置只能使用字元'\'或字元'd'
// 其實很簡單,用[]就行
Regex regex18 = new Regex("[\d]");//如上報錯:CS1009:無法識別的轉義序列。
// 一開沒轉義,改為
Regex regex19 = new Regex("[\\d]");//編譯通過,完活。
string str10 = @"a我打[]{}aa\bb""cc''dd^ee/dff\\";
Match result19 = regex19.Match(str10);// 匹配失敗
// 然而result19的結果是 匹配失敗。
// 這就是字元轉義與正則轉義的混合。
// '\\d'僅處理了字串中'\'的問題,沒有解決正則'[\d]'中'\'也需要轉義的問題;
// 想要實現一個位置只能使用字元'\'或字元'd',正確的正則表達應該為[\\d]
// 那麼再將其放入C#字串中,每個'\'都要再轉義一次,即為
Regex regex20 = new Regex("[\\\\d]");
Match result20 = regex20.Match(str10);
// 或
Regex regex21 = new Regex(@"[\\d]");
Match result21 = regex21.Match(str10);
// 我還好奇了一下[]中有重複字元會怎樣
Regex regex22 = new Regex(@"[\\\\d]");
Match result22 = regex22.Match(str10);
// 結果好像沒啥區別,有相同字元無所謂的,沒影響
// 這裡後續使用又發現了一點,補充一下
// 前文提到:
Regex regex19 = new Regex("[\\d]");
string str10 = @"a我打[]{}aa\bb""cc''dd^ee/dff\\";
Match result19 = regex19.Match(str10);
// 會編譯通過,但是匹配失敗。這裡遺漏了一個問題,就是new Regex("[\\d]");到底再匹配什麼?
// 答案就是它再匹配'/d',也就是任意以為0~9的數字
string str101 = @"a我打[]{}aa\bb""cc''dd^ee/dff\\";
string str102 = @"a我打[]{}aa\bb""cc''d9d^ee/dff\\";//中間加了個9
Regex regex191 = new Regex("[\\d]");
Match result191 = regex191.Match(str101);//匹配失敗
Match result192 = regex191.Match(str102);//匹配成功,找到9
//所以[],不止能匹配[]中的實際內容,還可以配合元字元匹配所有那一類字元
[]
搭配-
可以表示連續的字元
Regex regex22 = new Regex("[0-3]");// 某位置匹配0~3,也就是0,1,2,3
[]
搭配^
可以表示排除
Regex regex23 = new Regex("[^0-3]");// 某位置匹配除了0,1,2,3都可以。
可以複習一下要是就想匹配-
,^
,甚至[]
怎麼辦?
Regex regex24 = new Regex("[\\^\\[\\]\\-]");
//或
Regex regex25 = new Regex(@"[\^\[\]\-]");
這裡實際試了一下這些特殊字元,除了[ 和 ]
不轉義也能匹配,迷惑。
最後就是(),()有很多功能,包括限制多選結構的範圍,分組,捕獲文字,環視,特殊模式處理。
我感覺比較基礎的使用就是限制多選與分組。
// 匹配必須整段一模一樣的abc或bcd或cde,
Regex regex26 = new Regex("(abc|bcd|cde)");
// 匹配必須有連續2個adb的重複,也就是abcabc,abcaabc不行
Regex regex27 = new Regex("(abc){2}");