# How to match and count

**URL:** <https://www.ruby-forum.com/t/how-to-match-and-count/179320>\
**Category:** Ruby\
**Created:** [November 22, 2009, 3:10am UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320 "2009-11-22T03:10:16Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![rubynut](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/8e8cbc/32.png) [@rubynut](https://www.ruby-forum.com/u/rubynut)\
**Post date:** [November 22, 2009, 3:10am UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320/1 "2009-11-22T03:10:16Z")

</div>

Hello,

I have been using awk for some text handling.  
Now I’m beginning Ruby (really newbie) and want to find a way in ruby  
to do this with awk:

awk ‘{if ($4~/something/) {i+=1}} END {print i}’ file.txt

That means if a line’s 4th field match “something” then increase the  
counter by 1.  
How to write the corresponding ruby code?

Thanks in advance.

---

<div class="post-metadata">

**Author:** ![rubynut](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/8e8cbc/32.png) [@rubynut](https://www.ruby-forum.com/u/rubynut)\
**Post date:** [November 22, 2009, 4:00am UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320/2 "2009-11-22T04:00:37Z")

</div>

Yes, thanks.

2009/11/22 Phrogz [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

---

<div class="post-metadata">

**Author:** ![rubynut](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/8e8cbc/32.png) [@rubynut](https://www.ruby-forum.com/u/rubynut)\
**Post date:** [November 22, 2009, 5:54am UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320/3 "2009-11-22T05:54:11Z")

</div>

On Nov 21, 8:00 pm, Ruby N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> 2009/11/22 Phrogz [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):
> 
> > On Nov 21, 7:09 pm, Ruby N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:
> > 
> > > awk ‘{if ($4~/something/) {i+=1}} END {print i}’ file.txt  
> > > That means if a line’s 4th field match “something” then increase the  
> > > counter by 1.  
> > > How to write the corresponding ruby code?  
> > > What is a ‘field’? Whitespace delimited?  
> > > Yes, thanks.

Here are two ways:

# Don’t read the whole file into memory, but do it one line at a time

i = 0  
file = File.open( “foo.txt” )  
file.each\_line do |line|  
pieces = line.split( /\s+/ )  
i += 1 if pieces[3] =~ /something/  
end

# Just read the whole file at once, assuming it’s small enough,

# and create an array of the fourth column’s

col = File.read(“foo.txt”).scan(/.+/).map{ |line| line.scan(/\S+/)  
[3] }  
i = col.count{ |val| val =~ /something/ }

---

<div class="post-metadata">

**Author:** ![rubynut](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/8e8cbc/32.png) [@rubynut](https://www.ruby-forum.com/u/rubynut)\
**Post date:** [November 22, 2009, 10:18am UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320/4 "2009-11-22T10:18:41Z")

</div>

2009/11/22 Phrogz [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> # Don’t read the whole file into memory, but do it one line at a time
> 
> i = 0  
> file = File.open( “foo.txt” )  
> file.each\_line do |line|  
> Â pieces = line.split( /\s+/ )  
> Â i += 1 if pieces[3] =~ /something/  
> end

I like that, thank you!

---

<div class="post-metadata">

**Author:** ![rubynut](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/8e8cbc/32.png) [@rubynut](https://www.ruby-forum.com/u/rubynut)\
**Post date:** [November 22, 2009, 3:51am UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320/5 "2009-11-22T03:51:50Z")

</div>

On Nov 21, 7:09 pm, Ruby N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> awk ‘{if ($4~/something/) {i+=1}} END {print i}’ file.txt
> 
> That means if a line’s 4th field match “something” then increase the  
> counter by 1.  
> How to write the corresponding ruby code?

What is a ‘field’? Whitespace delimited?

---

<div class="post-metadata">

**Author:** ![rubynut](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/8e8cbc/32.png) [@rubynut](https://www.ruby-forum.com/u/rubynut)\
**Post date:** [November 22, 2009, 3:55pm UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320/6 "2009-11-22T15:55:44Z")

</div>

On 22.11.2009 10:12, Ruby N. wrote:

> 2009/11/22 Phrogz [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):
> 
> > # Don’t read the whole file into memory, but do it one line at a time
> > 
> > i = 0  
> > file = File.open( “foo.txt” )  
> > file.each\_line do |line|  
> > pieces = line.split( /\s+/ )  
> > i += 1 if pieces[3] =~ /something/  
> > end

> I like that, thank you!

The code above does not close the file handle properly. Also if can be  
done shorter:

File.foreach “file.txt” do |line|  
…  
end

You can even use Ruby like awk which seems to be rarely done - but it’s  
possible.

> awk ‘{if ($4~/something/) {i+=1}} END {print i}’ file.txt

Can be done like

ruby -nae ‘BEGIN {$i=0}; $i+=1 if /something/ =~ $F[3]; END {puts $i}’  
file.txt  
ruby -nae ‘BEGIN {$i=0}; /something/ =~ $F[3] and $i+=1; END {puts $i}’  
file.txt

For a script, I’d probably do something similar to what Phrogz suggested  
but with the difference that I’d use ARGF. That way you fetch file  
names from the command line and do not need to change the script if the  
file name changes:

i = 0

ARGF.each do |line|  
bit = line.split(/\s+/)[3]  
i += 1 if /something/ =~ bit  
end

puts i

Or, do the matching in one step which seems more efficient

i = 0

ARGF.each do |line|  
i += 1 if /^\s\*(?:\S+\s+){3}something/ =~ line  
end

puts i

There are about 2,843 million other ways to do it in Ruby.

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![rubynut](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/8e8cbc/32.png) [@rubynut](https://www.ruby-forum.com/u/rubynut)\
**Post date:** [November 23, 2009, 6:33am UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320/7 "2009-11-23T06:33:00Z")

</div>

2009/11/22 Robert K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> ruby -nae ‘BEGIN {$i=0}; $i+=1 if /something/ =~ $F[3]; END {puts $i}’  
> file.txt  
> ruby -nae ‘BEGIN {$i=0}; /something/ =~ $F[3] and $i+=1; END {puts $i}’  
> file.txt

oops this is the same way as Perl’s.

I may adjust one point:

should be:  
$F[3] =~ /something/;

not:  
/something/ =~ $F[3];

After that replace “ruby” to “perl” on above commands and that will be  
working too.

Thanks~

---

<div class="post-metadata">

**Author:** ![rubynut](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/8e8cbc/32.png) [@rubynut](https://www.ruby-forum.com/u/rubynut)\
**Post date:** [November 23, 2009, 9:33am UTC](https://www.ruby-forum.com/t/how-to-match-and-count/179320/8 "2009-11-23T09:33:49Z")

</div>

2009/11/23 Ruby N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> I may adjust one point:
> 
> should be:  
> $F[3] =~ /something/;
> 
> not:  
> /something/ =~ $F[3];

Why?

> After that replace “ruby” to “perl” on above commands and that will be  
> working too.

Ah, you want a single program to work both for Perl and Ruby. Thanks  
for sharing!

I usually prefer to have the regular expression as the first argument  
to =~ because for me that seems more natural (the regexp is doing the  
matching) and IIRC it is a tad faster (but really only a tad).

Kind regards

robert
