【问题标题】:RUBY: Search and Find in text file, remove blocks of textRUBY:在文本文件中搜索和查找,删除文本块
【发布时间】:2021-06-08 04:25:19
【问题描述】:

我目前正在为一个工作项目而苦苦挣扎。简而言之,我们有一个由软件自动生成的 300 万行长(因此无法手动编辑)的大文本文件。此文本文件描述了变量,其格式如下:

/begin var_type var_name
[content of variable]
/end var_type

但是,由于生产线上的一些混乱,一些变量被重复,这给最终用户带来了困扰。这就是我们所拥有的:

/begin var_type var_name              //the original variable
[content of variable]
/end var_type

过了一会儿,我们有了

/begin var_type var_name_ext          //same type, same name but with "_ext" at the end
[same content of variable]
/end var_type

我不是一个伟大的开发者,但我认为算法应该:

1: search for every name of variables_ext
2: check if they indeed have a "non _ext" counterpart"
         -> if not (there is a var_name_ext but no non _ext counterpart), leave them alone
         -> if yes, the algorithm does what the client wants, i.e:
                        a: delete original variable block (from /begin to /end)
                        b: delete "_ext" name extension in the name of the var_name_ext variable

我陷入困境的部分是,出于集成目的,这需要在 Ruby 中完成,这是一种我熟悉但并不真正精通的语言,因为我是初学者。我想我需要使用正则表达式,但我无法真正掌握在我的情况下实现它的方法。 当控制台输出看到 /begin 或 /end 标签时,我设法进行基本搜索,将“true”打印到控制台输出,但我真的坚持算法的实现。

感谢任何帮助/建议,谢谢!

【问题讨论】:

    标签: regex ruby search text


    【解决方案1】:

    问题是您必须执行两次传递,第一次搜索“ext”定义,第二次删除覆盖的值。

    你可以这样继续

    require 'set'
    
    f = File.open(filename)
    vars = Set.new(f.each_line.grep(/^\/begin /)) { |line| line.split.last }
    
    overridden = vars.select do |var|
      vars.include?("#{var}_ext") && vars.include?(var)
    end
    
    overridden = Set.new(overridden)
    to_rename = Set.new(overridden) { |var| "#{var}_ext" }
    
    f.close
    

    现在您已经拥有了所有被覆盖的变量,您可以重新打开文件并进行转换

    f = File.open(filename)
    
    out = File.open(output_filename, 'w')
    
    skipping = false # use it to skip block of lines
    
    f.each_line do |line|
      next if skipping
    
      if line.start_with?('/begin ') && overridden.include?(line.split.last)
        skipping = true
      elsif line.start_with?('/end ') && overridden.include?(line.split.last)
        skipping = false
      elsif line.start_with?('/begin ') && to_replace.include?(line.split.last)
        out << line.gsub(/_ext\n/, "\n")
      elsif line.start_with?('/end ') && to_replace.include?(line.split.last)
        out << line.gsub(/_ext\n/, "\n")
      else
        out << line
      end
    end
    
    out.close
    f.close
    

    基本上你在一个新文件中写出来,前提是你不在一个被覆盖的块中,在那里你使用一个标志来防止写入。

    【讨论】:

    • 谢谢,已修改 :) 我是在 math.stackexchange 上回答的,所以我的手指处于“LaTeX”模式?
    • 关于关闭,如果这是一个孤立的脚本,文件将被垃圾收集,并且只使用了 3 个文件描述符。虽然我看到了通常关闭打开文件的意义,但我不想让答案过于复杂。
    【解决方案2】:

    让我们创建一个用于演示的示例。

    File.write('in', <<~END
    /begin d1 collie
    collie woof
    /end d1
    /begin d2 pug
    pug woof
    /end d2
    /begin d3 beagle
    beagle woof
    beagle woof-woof
    /end d3
    /begin d1 collie_ext
    collie woof
    /end d1
    /begin d4 poodle_ext
    poodle woof
    /end d4
    END
    )
      #=> 204
    

    现在让我们读取文件,以便创建一个指定如何修改文件的哈希。

    remove_ext =
      File.foreach('in', chomp: true).with_object({}) do |line,remove_ext|
        next unless line.start_with?('/begin ')
        key = line[7..-1]
        if key[-4..-1] == '_ext'
          base = key[0..-5]
          if remove_ext.key?(base)
            remove_ext.delete(base)
            remove_ext[key] = true
          else
            remove_ext[key] = false
          end
        else
          remove_ext[key] = false
        end
      end
        #=> {"d2 pug"=>false, "d3 beagle"=>false,
        #    "d1 collie_ext"=>true, "d4 poodle_ext"=>false}
    

    我们现在可以创建所需的文件了。

    File.open('out', 'w') do |fout|
      write_lines = true
      File.foreach('in', chomp: true) do |line|
        if line.start_with?('/begin ')
          key = line[7..-1]
          if remove_ext.key?(key)
            write_lines = true
            line = line[0..-5] if remove_ext[key]
          else
            write_lines = false
          end
        end
        fout.puts(line) if write_lines
      end
    end
    

    让我们看看写了什么。

    puts File.read('out')
    /begin d2 pug
    pug woof
    /end d2
    /begin d3 beagle
    beagle woof
    beagle woof-woof
    /end d3
    /begin d1 collie
    collie woof
    /end d1
    /begin d4 poodle_ext
    poodle woof
    /end d4
    

    如果存在已知整数N,则可以采用具有较低内存要求的替代方法,如果“非扩展”块是ith 块和相应的“扩展”条目,如果存在一,是jth 块,i &lt; j,然后是j - i &lt;= N。换句话说,“稍后”意味着“在N 块内”。

    在这种情况下,要确定是否将一个块写入新文件,无论是否带有“ext”扩展名,人们将检查缓存“下一个”N 块(或所有如果文件中剩余的块少于N)。


    附录:要删除评论中提到的额外换行符,请尝试以下操作。

    File.write('bad', <<~END
    /begin d1 collie
    collie woof
    /end d1
    /begin d2
    
     pug
    pug woof
    /end d2
    /begin d3 
    
    
    beagle
    beagle woof
    beagle woof-woof
    /end d3
    END
    )
    
    puts File.read('good')
    /begin d1 collie
    collie woof
    /end d1
    /begin d2 pug
    pug woof
    /end d2
    /begin d3 beagle
    beagle woof
    beagle woof-woof
    /end d3
    

    【讨论】:

    • 嘿,感谢您提供非常完整的答案,我有一个小问题不清楚:您能解释一下 key[-4..-1] == '_ext' 语法吗?是因为我们想“提取”最后 3 个字符,所以从 -1 到 -4?如果我们想删除一个 n 长的后缀,会是 [-(n+1)..-1] 吗?
    • 更新:刚刚实现了你的版本,效果很好!我正朝着字典方法前进,在那里我会有一个包含 _ext 变量名的文件,但你的似乎更简单。但是,有一个我忘记解决的问题。格式似乎不一致,即有时,变量类型和变量名之间可能有一两个回车符/换行符...您将如何适应这种情况?
    • strstr[-n..-1] = ''str = str[0..-(N+1)] 中删除一个n 长的后缀。关于变量类型和名称之间不需要的字符,我倾向于删除不需要的字符作为初始步骤。如果您想要代码,请告诉我。
    • 感谢您的清理!不想要的字符的代码将不胜感激,谢谢!
    • 请详细说明换行符的问题。可能发生以下哪一项:'/begin d1\r\n pug''/begin d1 \r\npug''/begin d1\r\npug''/begin d1\r\n\r\n pug''/begin d1 \r\n\r\npug''/begin d1\r\n \r\npug''/begin d1\r\n\r\npug'、上述所有以\n 替换\r\n 和所有前面用'/end' 代替'/begin'?在指定代码要求时养成精确的习惯。
    【解决方案3】:

    将每个文本变量推入一个关联哈希数组,它将删除重复的变量内容。不需要做任何功能。

    试试这个方法,对你有帮助。

    以下是示例语法,它将忽略最后一个“TextContent2”:

    harray = Hash.new
    harray["TextContent1"] = '1' 
    harray["TextContent2"] = '1' 
    harray["TextContent3"] = '1'
    harray["TextContent2"] = '1'
    

    【讨论】:

    • 关联散列数组是什么意思?你能再解释一下吗?我猜它是一种数据结构类型,如何实现它?
    • 它将以独特的方式保存密钥。如果您将文本内容推送到哈希数组键中,它将在推送该数组时忽略重复键。最后,您将拥有独特的文本。参考rubylearning.com/satishtalim/ruby_hashes.html
    • “关联散列数组”——在 ruby​​ 中,我们只称它为“散列”:)
    • '它将忽略最后一个“TextContent2”' - 不,它不会。事实上恰恰相反。
    猜你喜欢
    • 1970-01-01
    • 2015-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-02
    相关资源
    最近更新 更多