3
votes

I have somefile.txt, contains lines like:

{ abc1 } 1
{ cde1 } 101
{ fgh1 } 1
{ ijk1 } 2 

its a huge file, i wanted to find only 1st and 3rd line and count them.

I have tried with regexp and lsearch(converting it to list) by {\s\}\s1\n} but its not working. What should I do...?

I have also tried {\s\}\s1} but it prints all 4 lines.

3
You question is very unclear: what output do you want to see? - glenn jackman
So, you want to sum up 1+1? - Wiktor Stribiżew
OK, I wanted see only those line which ends with 1(only 1, not even 111,101 or 100) outside the curly brackets. There are millions of lines, I have shown here only four, inside the bracket characters like "",[],/, e.g { a/b"[c] } 1 are also there. - ShivankG

3 Answers

1
votes

Solution 1: If you dont want to use regexp and your inputs line have same format like {string} number

set fd [open "somefile.txt" r]
while {[gets $fd line] >= 0} {
    if {[lindex $line 1] == 1} {
        puts [lindex $line 1] ;# Prints only 1
        puts $line            ;# Prints Whole Line which has 1 at end
    }
}

Solution 2: If you want to use regexp, then go for group-capturing which is (.*)

set fd [open "somefile.txt" r]
while {[gets $fd line] >= 0} {
    if {[regexp "\{.*\} (.*)" $line match match1]} {
        if {$match1 == 1} {
            puts $line
        }
    }
}

Solution 3: Based on @Peter suggestion on regexp

set fd [open "somefile.txt" r]
while {[gets $fd line] >= 0} {
    if {[regexp {\d+$} $line match]} {
        if {$match == 1} {
            puts $match ;# Prints only 1
            puts $line  ;# Prints whole line which has 1 at end 
        }
    }
}
0
votes

You seem to need to capture the digits at the end of the first and third lines.

Here is a way to achieve that:

set s {{ abc1 } 1
{ cde1 } 101
{ fgh1 } 1
{ ijk1 } 2}
set re {^{[^{}]*}\s*(\d+)\s+{[^{}]*}\s*\d+\s+{[^{}]*}\s*(\d+)}
regexp $re $s m g1 g2
set res [expr $g1 + $g2]
puts $res

See the IDEONE demo

The pattern matches:

  • ^ - start of a string
  • {[^{}]*} - a {...}-like string with no braces inside
  • \s* - 0+ whitespaces
  • (\d+) - Group 1 (g1) capturing 1+ digits
  • \s+ - 1+ whitespaces (can be replaced with [\r\n]+ if there can be no trailing/leading whitespace before and after)
  • {[^{}]*}\s*\d+\s+{[^{}]*}\s*(\d+) - see above, just (\d+) will create a second variable, g2.

See the regex demo

0
votes

A problem like this gets around an order or magnitude easier to solve if you don't use regular expressions.

package require fileutil

::fileutil::foreachLine line somefile.txt {
    if {[lindex $line end] == 1} {
        puts $line
    }
}

This solution looks at each line in the file and checks if the last item is equal to 1. If so, the line is printed.

You could also count them / sum them:

set count 0
set sum 0
::fileutil::foreachLine line somefile.txt {
    if {[lindex $line end] == 1} {
        puts $line
        incr count
        incr sum [lindex $line end] ;# yeah, I know, always 1
    }
}
puts "Number of lines: $count"
puts "Sum of items: $sum"

If fileutil isn't available in your Tcl installation and you can't or don't want to install it, you can use the lower-level core equivalent:

set f [open somefile.txt]
while {[gets $f line] >= 0} {
    if {[lindex $line end] == 1} {
        puts $line
    }
}
close $f

If you absolutely must use a regular expression, in this case you could do this:

::fileutil::foreachLine line somefile.txt {
    if {[regexp {\m1$} $line]} {
        puts $line
    }
}

This regular expression finds lines that end with the digit 1 in a word by itself (i.e. there are no digits or word characters preceding it).

Documentation: close, fileutil package, gets, if, lindex, open, package, puts, Syntax of Tcl regular expressions, regexp, while