搜索/替换/删除Jekyll YAML Front Matter类别标签

2024-04-27 23:22:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我继承了一个Jekyll网站,而且我来自一个.NET世界,所以对我来说这是一个学习曲线。在

这个Jekyll网站需要花费很多时间才能建立,我认为这是因为有成千上万的分类标签需要删除这些页面。我可以得到一个所有类别的列表,并创建了一个CSV,我想循环通过,并确定是否仍然需要一个类别标记。CSV的结构是:

old_tag,new_tag

很明显,我想基于这些更新标签(例如,将所有的C#,C-Sharp,C#和C-Sharp类别都改为C-Sharp)。但是,我还想删除一些旧标记字段存在而新标记字段为空的位置:

^{pr2}$

使用Ruby或Python,我想知道如何遍历4000多个降价文件,并使用CSV有条件地更新每个文件。我身上的数据库人员就是想不出这将如何处理平面文件。在


Tags: 文件csv标记net网站tag时间世界
2条回答

这里有一种可能的方法;不确定它对大量数据的处理效果如何:

require "stringio"
require "csv"

class MarkdownTidy
  def initialize(rules)
    @csv = CSV.new(rules.is_a?(IO) ? rules : StringIO.new(rules))
    @from_to = {}.tap do |hsh|
      @csv.each do |from, to|
        re = Regexp.new(Regexp.escape(from.strip))
        hsh[re] = to.strip
      end
    end
  end

  def tidy(str)
    cpy = str.dup
    @from_to.each do |re, canonical|
      cpy.gsub! re, canonical
    end
    cpy
  end
end

csv = <<-TEXT
C#, C-Sharp
C Sharp, C-Sharp
Crazy, 
C #, C-Sharp
TEXT

markdown = <<-TEXT
C# some text C # some text Crazy
C#, C Sharp
TEXT

mt = MarkdownTidy.new(csv)
[markdown].each do |str|
  puts mt.tidy(markdown)
end

这样做的目的是用一个打开文件、读取文件并将其保存回磁盘的循环来替换末尾的循环。在

我建议从哈希开始,像翻译表一样使用它。哈希查找非常快,可以很好地组织标记及其替换。在

hash = {
  # old_tag => new_tag
  'C#'      => 'C-Sharp',
  'C Sharp' => 'C-Sharp',
  'Crazy'   => '',
  'C #'     => 'C-Sharp',
}

您可以看到值中存在大量冗余,可以通过反转哈希值来修复,这样可以很好地减少它:

^{pr2}$

'Crazy'是一个离群值,但我们将处理它。在

Ruby的String.gsub有一个很好但很少使用的特性,我们可以向它传递一个正则表达式和一个哈希,它将用哈希中的等效值替换所有正则表达式匹配项。我们可以轻松构建正则表达式:

regex = /(?:#{ Regexp.union(hash.keys).source })/
=> /(?:C\-Sharp)/

现在,你可能会说,“等等,我还有很多标签要找!”,而且,由于哈希的构建方式,它们隐藏在值中。为了弥补这一点,我们将反转哈希的键和值,将值数组分解为各自的元素:

reversed_hash = Hash[hash.flat_map{ |k,v| v.map{ |i| [i,k] } }]
=> {
         "C#" => "C-Sharp",
    "C Sharp" => "C-Sharp",
        "C #" => "C-Sharp",
}

通过合并“特殊情况”的第二个散列,添加'Crazy'很容易:

special_cases = {
  'Crazy' => ''
}

reversed_hash = Hash[hash.flat_map{ |k,v| v.map{ |i| [i,k] } }].merge(special_cases)
=> {
         "C#" => "C-Sharp",
    "C Sharp" => "C-Sharp",
        "C #" => "C-Sharp",
      "Crazy" => ""
}

将其与regex构建代码一起使用:

regex = /(?:#{ Regexp.union(reversed_hash.keys).source })/
=> /(?:C\#|C\ Sharp|C\ \#|Crazy)/

它将使用自动生成的regex查找标记。如果需要区分大小写,请使用:

regex = /(?:#{ Regexp.union(reversed_hash.keys).source })/i

创建一些要测试的文本:

text =<<EOT
This is "#C#"
This is "C Sharp"
This is "C #"
This is "Crazy"
EOT
=> "This is \"#C#\"\nThis is \"C Sharp\"\nThis is \"C #\"\nThis is \"Crazy\"\n"

测试gsub

puts text.gsub(regex, reversed_hash)

哪些输出:

This is "#C-Sharp"
This is "#C-Sharp"
This is "#C-Sharp"
This is "#"

现在,我不太喜欢把大文件拖进内存中,因为这样做的伸缩性不好。今天的机器通常有很多GB的内存,但是我看到的文件仍然超过了机器的RAM。因此,我建议使用File.read来加载文件,然后使用一个gsub来处理它,而不是使用File.foreach。使用它可以更改代码。在

我是这样做的:

file_to_read = '/path/to/file/to/read'
File.open(file_to_read + '.new', 'w') do |fo|
  File.foreach(file_to_read) do |li|
    fo.puts li.gsub(regex, reversed_hash)
  end
end

File.rename(file_to_read, file_to_read + '.bak')
File.rename(file_to_read + '.new', file_to_read)

这将为所处理的每个文件创建一个.bak版本,因此如果出现问题,您可以进行回退,这通常是一个好的做法。在


编辑:我忘了CSV文件:

您可以使用CSV模块轻松地使用Ruby读取/创建一个,但是我还是选择YAML文件,因为它允许您在易于手工编辑或从文件生成的文件中轻松创建哈希布局。在


编辑:更多关于CSV,YAML和从另一个生成一个

以下是如何读取CSV并将其转换为建议的哈希格式:

require 'csv'

text = <<EOT
C#, C-Sharp
C Sharp, C-Sharp
Crazy,
C #, C-Sharp
EOT

hash = Hash.new{ |h,k| h[k] = [] }
special_cases = []
CSV.parse(text) do |k,v|
  (
    (v.nil? || v.strip.empty?) ? special_cases : hash[v.strip]
  ) << k.strip
end

从之前取货:

reversed_hash = Hash[hash.flat_map{ |k,v| v.map{ |i| [i,k] } }].merge(Hash[special_cases.map { |k| [k, ''] }])
puts reversed_hash
# => {"C#"=>"C-Sharp", "C Sharp"=>"C-Sharp", "C #"=>"C-Sharp", "Crazy"=>""}

要将CSV文件转换为更可编辑和更有用的文件,请使用上面的代码创建hash和{},然后:

require 'yaml'

puts ({
  'hash' => hash,
 'special_cases' => special_cases
}).to_yaml

看起来像:

 -
hash:
  C-Sharp:
  - C#
  - C Sharp
  - ! 'C #'
special_cases:
- Crazy

剩下的你可以从YAML文档中找到。在

相关问题 更多 >