как перезаписать часть строки в текстовом файле с помощью регулярных выражений и .sub в ruby - PullRequest
0 голосов
/ 21 января 2019

У меня есть следующий макет в текстовом файле.

[item] label1: comment1 | label2: foo

У меня есть код ниже. Цель состоит в том, чтобы изменить часть существующей строки в тексте

def replace_info(item, bar)
return "please create a file first" unless File.exist?('site_info.txt')
    IO.foreach('site_info.txt','a+') do |line|
        if line.include?(item)
            #regex should find the data from the whitespace after the colon all the                     way to the end.
            #this should be equivalent to foo
            foo_string = line.scan(/[^"label2: "]*\z/)
            line.sub(foo_string, bar)
            end
        end
end

Пожалуйста, сообщите. Возможно, мой regrex выключен, но .sub правильный, но я не могу перезаписать line.

1 Ответ

0 голосов
/ 21 января 2019

Крошечная проблема: ваше регулярное выражение не делает то, что вы думаете./[^"label2: "]*\z/ означает: любое количество символов в конце строки, которые не a, b, e, l, ", пробел, двоеточие или 2 (см. Символклассы ).И scan возвращает массив, с которым sub не работает.Но это не имеет значения, потому что ...

Небольшая проблема: line.sub(foo_string, bar) ничего не делает.Он возвращает измененную строку, но вы ничего не назначаете, и она выбрасывается.line.sub!(foo_string, bar) изменит сам line, но это приводит нас к ...

Большая проблема: вы не можете просто изменить строку чтения и ожидать, что она изменится в самом файле.Это все равно что читать книгу, думать, что можно написать строчку лучше, и ожидать, что она изменит книгу.Чтобы изменить строку в текстовом файле, нужно прочитать один файл и скопировать прочитанное в другой .Если вы измените грань между чтением и записью, вновь написанная копия будет отличаться.В конце вы можете переименовать новый файл в старый (что приведет к удалению старого файла и его атомной замене новым).

РЕДАКТИРОВАТЬ: Вот некоторый код.Во-первых, мне не нравится IO.foreach, так как мне нравится самому управлять итерацией (и IMO, IO.foreach не читается как IO#each_line).В регулярном выражении я использовал lookbehind, чтобы найти метку, не включая ее в совпадение, поэтому я могу заменить только значение;Я изменил на \Z по той же причине, чтобы исключить перевод строки из матча.Вы не должны возвращать сообщения об ошибках из функций, для этого есть исключения.Я изменил простой include? на #start_with?, потому что ваш item может быть найден в другом месте строки, когда мы не хотим запускать изменение.

class FileNotFoundException < RuntimeError; end

def replace_info(item, bar)
  # check if file exists
  raise FileNotFoundException unless File.exist?('site_info.txt')

  # rewrite the file
  File.open('site_info.txt.bak', 'wt') do |w|
    File.open('site_info.txt', 'rt') do |r|
      r.each_line do |line|
        if line.start_with?("[#{item}]")
          line.sub!(/(?<=label2: ).*?\Z/, bar)
        end
        w.write(line)
      end
    end
  end

  # replace the old file
  File.rename('site_info.txt.bak', 'site_info.txt')
end

replace_info("item", "bar")
...