Regular Expression(正規表達式),是一種抽象語言,用來表達字串的結構,比方[A-Za-z]+能表達所有的英文單字,應用上常用來驗證輸入,或是捕捉(Capture)具有特徵的字串片段,利用Regular Expression可以大幅簡化字串處理的繁瑣作業
這次的問題如下,假設某種字串格式的具體內容是以前綴(prefix)後綴(post-fix)框住,怎麼利用正規表示式驗證並捕捉(Capture)重複的片段?
好比以下類似座標的紀錄檔:
Model:M1
Coordinates:
X1.62 Y1.01
X9.53 Y2.3
X4.94 Y2.1
X8.55 Y3.15
首先分析一下文本的結構:
其中"[-+]?[0-9]*\.?[0-9]+"就是浮點數的表達
這樣丟下去REGEX跑字串,模式字串中的()可以透過Match.Group(Index)存取,在這裡的模式字串我們共用到三個(),按照()在模式字串裡的順序,Group依序是:
Model:[\\w]+[\\s]+Coordinates:[\\s]+(?<coord>X(?<X>[-+]?[0-9]*\\.?[0-9]+)[\\s]*Y(?<Y>[-+]?[0-9]*\\.?[0-9]+)[\\s]*)+
具名化後,便可以用Match.Group(Name)方式存取群組了
這次的問題如下,假設某種字串格式的具體內容是以前綴(prefix)後綴(post-fix)框住,怎麼利用正規表示式驗證並捕捉(Capture)重複的片段?
好比以下類似座標的紀錄檔:
Model:M1
Coordinates:
X1.62 Y1.01
X9.53 Y2.3
X4.94 Y2.1
X8.55 Y3.15
X2.56 Y1.25
X5.57 Y2.72
首先分析一下文本的結構:
- 標頭如同前兩行,形式與內容固定
- 重複資料部分按行表達
- 單筆座標以X<浮點數> Y<浮點數>表示
- 座標值是我們最在意的部分
- 希望可以一組一組捕捉分離,才好批次後處理解析數值
模式字串(Pattern)的設計想法如下:
- 浮點數的表達不用傷腦筋,網路上很容易查到
- 座標值的部分用()群組住單組X,Y座標(也是一種Symbol),後面以+表達此組Symbol會重複1到N次
- 單組座標內又再次以()群組住浮點數字串表達
- 考慮強健性,安插些泛空白字元在各個單元交接處
於是我們交出了以下的模式字串 :
"Model:[\\w]+[\\s]+Coordinates:[\\s]+(X([-+]?[0-9]*\\.?[0-9]+)[\\s]*Y([-+]?[0-9]*\\.?[0-9]+)[\\s]*)+"
其中"[-+]?[0-9]*\.?[0-9]+"就是浮點數的表達
這樣丟下去REGEX跑字串,模式字串中的()可以透過Match.Group(Index)存取,在這裡的模式字串我們共用到三個(),按照()在模式字串裡的順序,Group依序是:
- Index-0:原始字串
- Index-1:單組座標的群組
- Index-2:X座標浮點數字串
- Index-3:Y座標浮點數字串
String __input = "Model:M1 Coordinates: X1.62 Y1.01 X9.53 Y2.3 X4.94 Y2.1 X8.55 Y3.15 X2.56 Y1.25 X5.57 Y2.72"; String __patternUnnamed = "Model:[\\\\w]+[\\\\s]+Coordinates:[\\\\s]+(X([-+]?[0-9]*\\\\.?[0-9]+)[\\\\s]*Y([-+]?[0-9]*\\\\.?[0-9]+)[\\\\s]*)+"; List若模式字串裡()太多,或是覺得用INDEX標定群組可讀性太差,此時可以套入具名群組(Named Group)的概念,把()所圈住的Symbol具名化:__coords = new List (); Match __matchUnnamed = Regex.Match(__input,__patternUnnamed); //----------------------------------- // Unnamed //----------------------------------- __coords.Clear(); for (int i = 0; i < __matchUnnamed.Groups[1].Captures.Count;i++) { // use X,Y group restore each coordinates float[] eachCoord = new float[]{0,0}; eachCoord[0] = float.Parse(__matchUnnamed.Groups[2].Captures[i].Value); eachCoord[1] = float.Parse(__matchUnnamed.Groups[3].Captures[i].Value); __coords.Add(eachCoord); } //check all coordinates Console.WriteLine(\"Unnamed\"); foreach (float[] item in __coords) { Console.WriteLine(String.Format(\"{0},{1}\", item[0], item[1])); }
Model:[\\w]+[\\s]+Coordinates:[\\s]+(?<coord>X(?<X>[-+]?[0-9]*\\.?[0-9]+)[\\s]*Y(?<Y>[-+]?[0-9]*\\.?[0-9]+)[\\s]*)+
具名化後,便可以用Match.Group(Name)方式存取群組了
String __patternNamed = "Model:[\\\\w]+[\\\\s]+Coordinates:[\\\\s]+(?X(? [-+]?[0-9]*\\\\.?[0-9]+)[\\\\s]*Y(? [-+]?[0-9]*\\\\.?[0-9]+)[\\\\s]*)+"; Match __matchNamed = Regex.Match(__input,__patternNamed); //----------------------------------- // Named //----------------------------------- //now we get the \"coord\" group __coords.Clear(); for (int i = 0; i < __matchNamed.Groups[\"coord\"].Captures.Count; i++) { // use X,Y group restore each coordinates float[] eachCoord = new float[]{0,0}; eachCoord[0] = float.Parse(__matchNamed.Groups[\"X\"].Captures[i].Value); eachCoord[1] = float.Parse(__matchNamed.Groups[\"Y\"].Captures[i].Value); __coords.Add(eachCoord); }
後記1:.NET的正規表達式相容於Perl5,也與這裡相容,可以先快速的先驗表達式
後記2:據說.NET的Regular Expression模組是難得可以幫你Capture住群組值(Value)的函式庫
程式碼Repo
後記2:據說.NET的Regular Expression模組是難得可以幫你Capture住群組值(Value)的函式庫
程式碼Repo