3

私は Jekyll Web サイトを継承しており、.NET の世界から来たので、学習曲線です。

この Jekyll サイトの構築には永遠に時間がかかります。これは、ページを削除する必要のあるカテゴリ タグが文字通り何千もあるためだと思います。すべてのカテゴリのリストを取得し、ループしてカテゴリ タグがまだ必要かどうかを判断したい CSV を作成しました。CSV の構造は次のとおりです。

old_tag,new_tag

それらに基づいてタグを更新したいのは明らかです (たとえば、C#、C-Sharp、C#、および C Sharp のすべてのカテゴリを C-Sharp だけにします)。ただし、古いタグ フィールドが存在し、新しいタグ フィールドが空白になっている場所もいくつか削除したいと思います。

old_tag,new_tag
C#, C-Sharp
C Sharp, C-Sharp
Crazy, 
C #, C-Sharp

Ruby または Python を使用して、4000 を超えるマークダウン ファイルをループ処理し、CSV を使用して各ファイルを条件付きで更新する方法を見つけたいと思います。私のデータベース担当者は、これがフラット ファイルでどのように機能するか想像できません。

4

2 に答える 2

2

変換テーブルのように使用して、ハッシュから始めることをお勧めします。ハッシュ ルックアップは非常に高速で、タグとその置換を適切に整理できます。

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

値に多くの冗長性があることがわかります。これは、ハッシュを逆にすることで修正でき、うまく削減できます。

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

'Crazy'外れ値ですが、対処します。

RubyString.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'「特殊なケース」の 2 番目のハッシュをマージすることで、簡単に追加できます。

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 = /(?:#{ Regexp.union(reversed_hash.keys).source })/
=> /(?:C\#|C\ Sharp|C\ \#|Crazy)/

自動生成された正規表現を使用してタグを見つけます。大文字と小文字を区別しない必要がある場合は、次を使用します。

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 ファイルをより編集可能で便利なものに変換するには、上記のコードを使用して と を作成hashspecial_cases、次のようにします。

require 'yaml'

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

次のようになります。

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

残りは YAML ドキュメントから理解できます。

于 2012-12-20T00:05:52.823 に答える
0

考えられるアプローチの1つを次に示します。大量のデータに対してどれだけうまく機能するかわからない:

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

アイデアは、最後のループを、ファイルを開いて読み取り、ディスクに保存するループに置き換えることです。

于 2012-12-20T03:59:04.957 に答える