2012-12-19 3 views
3

저는 지킬 (Jekyll) 웹 사이트를 상속 받았으며 닷넷 세계에서 왔기 때문에 저를위한 학습 곡선이되었습니다.지킬 YAML 프론트 머터 범주 태그

이 Jekyll 사이트는 영원히 구축에 걸리며 문자 그대로 수천 개의 카테고리 태그가 삭제되어야한다고 요구하기 때문에 생각합니다. 나는 모든 카테고리의리스트를 얻을 수 있었고, 내가 반복하고 싶은 CSV를 만들었고 카테고리 태그가 여전히 필요한지 알아 냈다. CSV 파일 구조는 다음

old_tag,new_tag 

은 분명히 내가 (예를 들어, 모든 C#, C-샤프, C# 및 C 샤프 카테고리 단지 C-샤프을) 그 기반으로 태그를 업데이트하고 싶습니다. 그러나 나는 또한 기존의 태그 필드가 존재하고 새로운 하나가 비어있는 경우 일부를 삭제할 : 루비 나 파이썬을 사용하여 내가 원하는

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

알아낼 방법 4000 인하 파일을 통해 루프 및 CSV를 사용하여 조건에 따라 각각을 업데이트하십시오. 필자의 데이터베이스 담당자는 이것이 플랫 파일에서 어떻게 작동하는지 생각할 수 없다.

답변

1

번역 표와 같이 해시로 시작하는 것이 좋습니다. 해시 조회는 매우 빠르며 태그와 그 대체물을 멋지게 정리할 수 있습니다.

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'은 특이하다,하지만 우리가 다룰 것입니다 그.

루비의 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'에 추가

은 "특별한 경우"의 두 번째 해시를 병합하여, 쉽게 :

정규식 buildin '코드가 사용
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 버전을 생성합니다, 그래서 뭔가 잘못되면 당신은 항상 좋습니다 가을 백을 가지고 : 여기

내가 그것을 할 거라고 방법입니다.


편집 : 그것은 당신이 쉽게 만들 수 있기 때문에 그러나 나는 YAML 파일과 함께 가고 싶어,

당신은 읽기/루비는 CSV 모듈을 사용하여 쉽게 만들 수 있습니다 : 나는 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 

전에에서 따기 : CSV, YAML에 대한 자세한 다른

에서 하나를 생성하는 여기에 CSV를 읽고 권장 해시 형식으로 변환하는 방법 :

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 파일을 더 편집 가능하고 유용한 것으로 변환하려면 ab 비켜 코드는, hashspecial_cases 만들려면 다음

require 'yaml' 

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

과 같습니다

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

나머지 부분은 YAML 워드 프로세서에서 알아낼 수 있습니다.

+0

'# gsub'에 대한 두 번째 인수로 멋진 트릭입니다. 'reversed_hash'를 얻기 위해'Hash # invert'를 사용할 수 있습니까? '{1 => : a, 2 => : b} .invert # => {: a => 1, : b => 2}'. –

+0

'invert'는 간단한 'key'=> 'value' '조합으로 해시를 처리하지만이 해시에는 값 배열이 있습니다. 루비에서는 배열을 키로 허용하지만 해시는 키에 대한 객체를 허용하기 때문에 코드는 값의 각 요소를 자체 키로 변환해야합니다. –

0

가능한 접근 방법은 다음과 같습니다. 이 많은 양의 데이터를 작동하는 방법을 잘 모르겠 :

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 

아이디어는 매우, 파일을 열어 그들을 읽고 하나로 끝나는에서 루프를 대체 할 것입니다 후 디스크에 다시 저장합니다 .