2017-09-09 3 views
2

私は、ファイルを読んでいるを返し、内容は以下の通りではありません。のTcl正規表現は、すべての一致

Aug2017: 
-------------------------------------- 
    Name Age Phone 
-------------------------------------- 
    Jack 25 128736372 
    Peter 26 987840392 
-------------------------------------- 
Sep2017: 
-------------------------------------- 
    Name Age Phone 
-------------------------------------- 
    Jared 21 874892032 
    Eric 24 847938427 
-------------------------------------- 

だから私はすべての破線の間で情報を抽出し、リストに入れたかったです。私は情報を抽出するために、以下のTcl正規表現を使用しています$dataは、ファイルの内容を含むと仮定すると、:

regexp -all -inline -- {\s+\-{2,}\s+(.*?)\s+\-{2,}\s+} $data 

私が知っているように、返されたマッチした結果がfullMatchsubMatchを含むリストとして保存されます。

llengthコマンドでダブルチェックした場合、fullMatchsubMatchの2つしかありません。

llength $data 
2 

なぜ1つしかありませんsubMatch?以下のような5つの試合があるように思わ:

Aug2017: 
-------------------------------------- 
    Name Age Phone  --> 1st Match 
-------------------------------------- 
    Jack 25 128736372 
    Peter 26 987840392 --> 2nd Match 
-------------------------------------- 
Sep2017:     --> 3rd Match 
-------------------------------------- 
    Name Age Phone  --> 4th Match 
-------------------------------------- 
    Jared 21 874892032  
    Eric 24 847938427 --> 5th Match 
-------------------------------------- 

したがって、この場合には、私はlindexで2番目のリスト要素(subMatch)を選択しています。私が得た結果は、このようなものですが、それはコンテンツの先頭と末尾からマッチングされるよう

lindex [regexp -all -inline -- {\s+\-{2,}\s+(.*?)\s+\-{2,}\s+} $data] 1 

は、思わ:

Name Age Phone 
-------------------------------------- 
    Jack 25 128736372 
    Peter 26 987840392 
-------------------------------------- 
Sep2017: 
-------------------------------------- 
    Name Age Phone 
-------------------------------------- 
    Jared 21 874892032 
    Eric 24 847938427 

私の印象は正規表現を始めと試合順から一致する必要がありました文字列の終わり、なぜtcl正規表現がこのように動作しているのか分かりませんか?何か不足していますか?

**ここで達成したいのは、破線の区切り記号の間にデータを抽出することです。上のデータは単なる例です。

期待される結果:すべての一致

{ {Name Age Phone}  -->1st match 
    {Jack 25 128736372 
    Peter 26 987840392} -->2nd match 
    {Sep2017:}    -->3rd match 
    {Name Age Phone}  -->4th match 
    {Jared 21 874892032 
    Eric 24 847938427} -->5th match 
} 

にUPDATE含むリスト: を私は少し@glennで先読みし、提案を含めて、以下のように私のTCLの正規表現を変更しました:

regexp -all -inline -expanded -- {\s+?-{2,}\s+?(.*?)(?=\s+?-{2,}\s+?)} $data 

私が得た結果(10のサブマッチ):

{ {---------------------- 
    Name Age Phone}  -->1st match 
    {Name Age Phone}  -->2nd match 
    {---------------------- 
    Jack 25 128736372 
    Peter 26 987840392} -->3rd match 
    {Jack 25 128736372 
    Peter 26 987840392} -->4th match 
    {---------------------- 
    Sep2017:}    -->5th match 
    {Sep2017:}    -->6th match 
    ... 
    ... 
} 

予想される結果にかなり近いですが、予想される5つのサブマッチに完全に一致するように正規表現を使用する方法を理解したいと思います。

+0

Tclの正規表現は、Perlののとは異なり、どちらか完全に貪欲ですか完全に非貪欲で、その選択は**最初の**の量指定子によって行われます見た。 're_syntax'のmanページの[Matching]セクション(https://tcl.tk/man/tcl8.6/TclCmd/re_syntax.htm#M95)を慎重に読む必要があります。先頭の '\ s +'は、正規表現全体の欲張りの好みを設定しています。それを '\ s + 'に変更すれば?'あなたは望みの結果に近づきますが、奇数番号のサブマッチだけになります。これは、正規表現が2行目のハイフンを消費しているため、次の行には1行目のハイフンがなくなるためです。 –

+0

私が提供したドキュメンテーション・リンクのキー・センテンスは、「枝は、その中の最初の数量化されたアトムと同じ優先度を持ちます。 –

+0

@glennjackman:あなたの提案をありがとう。結果は期待されたものにかなり近いですが、私はまだ完全に5つのサブマッチでマッチさせる方法を理解したいと思います。更新された結果は、「更新」セクションに表示されます。 –

答えて

1

正規表現のマッチングは、この種の問題には適していません。あなたは、ある種のラインフィルターではるかに良いです。

正規表現ベースのフィルタ

は、密接にあなたの例の行にマッチした:

set f [open data.txt] 
while {[gets $f line] >= 0} { 
    if {[regexp {:} $line]} continue 
    if {![regexp {\d} $line]} continue 
    puts $line 
} 
close $f 

理由:唯一の月名ラインはコロン、ヘッダ行を持っており、セパレータは、それらには数字を持っていません。

正規表現に限り依存しないフィルタ:

set f [open data.txt] 
set skip 4 
while {[gets $f line] >= 0} { 
    if {$skip < 1} { 
     if {[regexp {\-{2,}} $line]} { 
      set skip 4 
     } else { 
      puts $line 
     } 
    } else { 
     incr skip -1 
    } 
} 
close $f 

このコードは、すべての行を読み込み、各月の初めに4行をスキップし、そして4にスキップをリセットしたときのラインダッシュがデータを中断します。

(注:式\-{2,}はダッシュが特殊な正規表現であり、その理由でエスケープする必要があるように見えます。実際には、ダッシュが式の最初の文字である場合、regexpコマンドは。スイッチとしてそれを解釈するためにregexp -- {-{2,}} ...があまりにも動作するだろうが、でも見知らぬ人に見える、私は思う)

ETA(コメントを参照してください):セパレータとの間でデータを取得する(つまり、単にセパレータをフィルタリング)、これを試してみてください。

set f [open data.txt] 
while {[gets $f line] >= 0} { 
    if {![regexp {\-{2,}} $line]} { 
     puts $line 
    } 
} 
close $f 

または:

package require fileutil 

::fileutil::foreachLine line data.txt { 
    if {![regexp {\-{2,}} $line]} { 
     puts $line 
    } 
} 

これも動作するはずです:改行を区別するマッチを有効にする

regsub -all -line {^\s+-{2,}.*(\n|\Z)} $data {} 

が、これは一致し、すべての空白のみ、ダッシュからなる行、任意の非改行のいずれか改行文字を削除しますまたは外側の文字列の終わりです。むしろ単に濾過ラインを印刷するよりも、マッチのリストを収集する

set matches {} 
set matchtext {} 
::fileutil::foreachLine line data.txt { 
    if {![regexp {\-{2,}} $line]} { 
     append matchtext $line\n 
    } else { 
     lappend matches $matchtext 
     set matchtext {} 
    } 
} 

これを実行した後、変数matchesは、そのアイテムのセパレータの間に連続線であるリストを含みます。

同じものにするもう一つの方法:

::textutil::splitx $data {(?n)^\s+-{2,}.*(?:\n|\Z)} 

(。また、それが問題であれば除去するのに十分に簡単である、リストの最後に空の要素を追加します)

ドキュメント: < (operator)>= (operator)appendclosecontinuefileutil (package)getsifincrlappendopenpackageputsregexpsettextutil (package)whileSyntax of Tcl regular expressions

+0

上記のデータは単なる例です。破線で区切った区切り文字の間にデータを抽出したいのであれば、どうすれば実現できますか?お知らせ下さい。 –

+0

@ user3635458:いくつかの提案が追加されました。 –

+0

ダッシュ文字を除外したくありません。私は、 '期待された結果'の下に追加したような、すべての ' - '区切りの間に単語を抽出したかったのです。私はこれを行単位で処理することで行うことができますが、私の意図は、抽出を行うために正規表現を使用することです。ありがとう。 –

関連する問題