0

There are lines inside a file that contain !. I need all other lines. I only want to print lines within the file that do not start with an exclamation mark.

The line of code which I have written so far is:

unless parts.each_line.split("\n" =~ /^!/)
  # other bit of nested code
end

But it doesn't work. How do I do it?

sawa
  • 165,429
  • 45
  • 277
  • 381
  • Welcome to Stack Overflow. We need a minimal example of data being checked and your expected output. See "[ask]" for the expected information for a question. – the Tin Man Oct 06 '15 at 17:33

2 Answers2

2

As a start I'd use:

File.foreach('foo.txt') do |li|
  next if li[0] == '!'
  puts li
end

foreach is extremely fast and allows your code to handle any size file - "scalable" is the term. See "Why is "slurping" a file not a good practice?" for more information.

li[0] is a common idiom in Ruby to get the first character of a string. Again, it's very fast and is my favorite way to get there, however consider these tests:

require 'fruity'

STR = '!' + ('a'..'z').to_a.join # => "!abcdefghijklmnopqrstuvwxyz"

compare do
  _slice { STR[0] == '!' }
  _start_with { STR.start_with?('!') }
  _regex { !!STR[/^!/] }
end

# >> Running each test 32768 times. Test will take about 2 seconds.
# >> _start_with is faster than _slice by 2x ± 1.0
# >> _slice is similar to _regex

Using start_with? (or its String end equivalent end_with?) is twice as fast and it looks like I'll be using start_with? and end_with? from now on.

Combine that with foreach and your code will have a decent chance of being fast and efficient.

See "What is the fastest way to compare the start or end of a String with a sub-string using Ruby?" for more information.

Community
  • 1
  • 1
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • `start_with` is faster than `[]` because no temporary string is created? Do you not prefer `puts li if li[0] != '!'` or `puts li unless li[0] == '!'`? A small quibble: I suggest `IO.foreach`, since `foreach` is defined in `IO` (and inherited by `File`). If readers want to look up the doc for `foreach` they may be puzzled when they can't find it in the docs for `File`. – Cary Swoveland Oct 06 '15 at 18:58
  • I do not like `puts li` with a trailing conditional for this use as it's visual noise, I'd rather the `puts` be stand-alone. Yes, File does inherit from IO, but that's true of a lot of OO code. Since I'm reading a File I use `File.foreach`. If I was doing it from a different type of stream my choice might be different. – the Tin Man Oct 06 '15 at 20:24
0

You can use string#start_with to find the lines that start with a particular string.

file = File.open('file.txt').read
file.each_line do |line|
  unless line.start_with?('!')
    print line
  end
end

You can also check the index of the first character

unless line[0] === "!"

You can also do this with Regex

unless line.scan(/^!/).length
Richard Hamilton
  • 25,478
  • 10
  • 60
  • 87
  • Don't use `read` then `each_line`; It's not scalable at all. Instead use `foreach` which is faster. `start_with?` is faster than an equivalent regular expression. Using `scan(/^!/).length` isn't a good choice as that's not what `scan` is for. If you want to know whether the line starts with `!` then do that directly: `line[/^!/]` but remember, that's slower than using `start_with?`. – the Tin Man Oct 06 '15 at 17:31