little-dude
little-dude

Reputation: 1674

variable number of match in a regexp

I have this file :

#2/1/1/21=p1 5/1/1/21=p1 isid=104
3/1/1/9=p1 4/1/1/4=p1 5/1/1/17=p1 6/1/1/4=p1 isid=100
1/1/1/4=p1 6/1/1/5=p1 isid=101

I want line 1 to be ignored (it a commented line) In line two I want to get "3/1/1/9" "4/1/1/4" "5/1/1/17" in three variables var1 var2 and var3 In line three I want to get "1/1/1/4" and "6/1/1/5" in two variables var1 and var2.

For the moment I can ignore line 1, and match what I want on line 2 OR line 3 :

if {[regexp {^(\d/\d/\d/\d{1,2})=p1.*(\d/\d/\d/\d{1,2})=p1} $line value match1 match2]} {  
    # This works for line 3 but not line 2
}

 if {[regexp {^(\d/\d/\d/\d{1,2})=p1.*(\d/\d/\d/\d{1,2})=p1.*(\d/\d/\d/\d{1,2})=p1} $line value match1 match2 match3]} {  
    # This works for line 2 but not line 3
}

How can I have the right number of matched for line 2 AND 3 ?

Upvotes: 1

Views: 122

Answers (3)

glenn jackman
glenn jackman

Reputation: 246837

This might be better:

lassign [regexp -all -inline {\d+(?:/\d+){3}(?==p1)} $line] var1 var2 var3

If there are only 2 matches, var3 will be empty

If there are more matches, only the first 3 will be captured in variables.

If you really just want all the matches:

set vars [regexp -all -inline {\d+(?:/\d+){3}(?==p1)} $line]

To ignore comments:

while {[gets $fh line] != -1} {
    if {[regexp {^\s*#} $line]} continue

    # do your other stuff here ...
}

Upvotes: 1

Jerry
Jerry

Reputation: 71538

Try this regex:

[regexp {^(\d/\d/\d/\d{1,2})=p1.*?(\d/\d/\d/\d{1,2})=p1(?:.*?(\d/\d/\d/\d{1,2})=p1)?} $line value match1 match2 match3]
                                                       ^^^                        ^^

This makes the third match optional.

I also turned your greedy .* into non-greedy .*?.


To get all matches, you could use something more like this:

if {[string range $line 0 0] ne "#"} {
    set matches [regexp -all -inline -- {\d/\d/\d/\d{1,2}(?==p1)} $line]
}

And you will get the matches in the list $matches. You can then access them through lindex or if you use lassign and give them specific names.

Upvotes: 2

Farmer Joe
Farmer Joe

Reputation: 6070

try this:

(^|\s)(\d/\d/\d/\d=p1)

or more compactly:

(^|\s)((\d/){3}\d=p1)

Upvotes: 0

Related Questions