Рубин заглянуть с включенным? действует как следующий

У меня проблемы с пониманием моего собственного сценария ruby ​​на данный момент. Если я проверю содержимое следующей позиции с peek, намереваясь пока не сдвинуть позицию, используя include?, позиция моего счетчика все равно перемещается на следующую.

Например:

print @file.each_line.peek
if @file.each_line.peek.include? 'State'
  ...

выходы

State

но это:

if @file.each_line.peek.include? 'State'
  print @file.each_line.peek
  ...

выходы

CO

Содержимое моего файла выглядит

...
Extension Date
State
CO
COLORADO
...

Я открываю этот файл следующим образом: @file = File.open(file) и используя @file.each_line переписчик.

Для меня это выглядит как использование @file.each_line.peek.include? 'State' фактически заставляет позицию двигаться на единицу. Кто-нибудь знает, почему это так и как мне этого избежать?

Как воспроизвести

Создайте файл с именем test.txt со следующим содержимым:

Extension Date
State
CO
COLORADO

Создайте файл с именем test.rb со следующим содержимым:

file = File.open('./test.txt')
until file.each_line.next.include? 'Extension Date' do ; end
print file.each_line.peek
if file.each_line.peek.include? 'State'
end

Когда вы бежите с ruby test.rb, вы должны получить вывод State,

Если затем переместить строку 3 так, чтобы она находилась внутри блока if, вывод (для меня) CO,

2 ответа

Решение

Это не .include?Это, как вы получите свой перечислитель (каждый раз новый). Заметим:

@file.each_line.peek # => "Extension Date\n"
@file.each_line.peek # => "State\n"
@file.each_line.peek # => "CO\n"
@file.each_line.peek # => "COLORADO\n"
@file.each_line.peek # => "\n"

Проблема здесь в том, что когда each_line называется, он читает строку. И поскольку между вызовами сохраняется позиция файла, при втором вызове он читает еще одну строку. И так далее.

Получите перечислитель один раз и держитесь за него.

enum = @file.each_line

enum.peek # => "Extension Date\n"
enum.peek # => "Extension Date\n"
enum.peek # => "Extension Date\n"
enum.peek # => "Extension Date\n"
enum.peek.include?('foo') # => false
enum.peek # => "Extension Date\n"

Давайте сначала запишем данные в файл.

FName = "temp"

File.write FName, "Extension Date\nstate\nCO\nCOLORADO\n\n"

Проверь это.

puts File.read FName
  # Extension Date
  # state
  # CO
  # COLORADO
  #

Теперь выполните ваш код с некоторыми puts Заявления добавлены.

file = File.open(FName)

loop do
  enum = file.each_line
  puts "enum = #{enum}"
  puts "enum's object id = #{ enum.object_id }"
  puts "file.pos = #{file.pos}"
  puts "enum.peek = #{enum.peek}"
  puts "enum.peek = #{enum.peek}"
end
file.close

печатает следующее. Первые пять строк:

enum = #<Enumerator:0x007feb528d8bd8>
file.pos = 0
enum's object id = 70324339525100
enum.peek = Extension Date
enum.peek = Extension Date

Положение файла (показано выше) изначально 0, Мы ищем, чтобы получить следующую строку в файле, который "Extention Date\n", Ruby должен продвигать указатель файла, чтобы прочитать первую строку кода. Однако она не сбрасывает позицию файла в ноль после того, как она это сделала, как можно увидеть ниже (file.pos #=> 0 + "Extention Date\n".size => 15). Мы также видим, что указатель файла не продвигается снова для второго enum.peekпредполагая, что Ruby хранит это значение в буфере.

enum = #<Enumerator:0x007feb528d8868>
enum's object id = 70324339524660
file.pos = 15
enum.peek = state

Создается новый перечислитель, как видно из возвращаемого значения из enum и изменился object_id, Этот перечислитель начинается со смещения файла 15, peek возвращается state\n продвигает указатель файла на 15 + "state\n".size #=> 21` (см. ниже).

enum = #<Enumerator:0x007feb528d84f8>
enum's object id = 70324339524220
file.pos = 21
enum.peek = CO

Создается третий перечислитель, начиная со смещения файла 21, peek возвращается CO\n, продвигая указатель файла на 21 + "CO\n".size #=> 24,

enum = #<Enumerator:0x007feb528d8188>
enum's object id = 70324339523780
file.pos = 24
enum.peek = COLORADO

Создается четвертый перечислитель, начиная со смещения файла 24, peek возвращается COLORADO\n, продвигая указатель файла на 24 + "COLORADO\n".size #=> 33,

enum = #<Enumerator:0x007feb528d3db8>
enum's object id = 70324339515100
file.pos = 33
enum.peek = 

Создается пятый перечислитель, начиная со смещения файла 33, peek возвращается \n, продвигая указатель файла на 33 + "\n".size #=> 34,

  # enum = #<Enumerator:0x007feb528d3a48>
  #   enum's object id = 70324339514660
  #   file.pos = 34

Шестой перечислитель создается, начиная со смещения файла 34, peek поднимает StopIteration исключение, которое обрабатывается ядром #loop путем выхода из цикла.

Очевидно, что вы не хотите продолжать создавать новые перечислители. Просто сделайте следующее.

file = File.open(FName)
enum = file.each_line

loop do
  line = enum.next
  puts line
end
file.close
  # Extension Date
  # state
  # CO
  # COLORADO

Я использовал Enumerator#next вместо Enumerator#peek, потому что для файлов они имеют одинаковый эффект и next лучше передает то, что делается.

Обратите внимание, что File.close часто пропускается, потому что Ruby закроет файл, когда дескриптор файла выйдет из области видимости.

Я предлагаю вам вместо этого использовать IO::foreach:

File.foreach(FName) do |line|
  puts line
end
  # Extension Date
  # state
  # CO
  # COLORADO
  #

foreach также читает файл построчно, но закрывает его при выходе из блока. Обратите внимание, что, потому что File это подкласс IO (File < IO #=> true), IO методы класса часто пишутся с File как получатель.

Другие вопросы по тегам