<p>我建议从哈希开始,像翻译表一样使用它。哈希查找非常快,可以很好地组织标记及其替换。在</p>
<pre><code>hash = {
# old_tag => new_tag
'C#' => 'C-Sharp',
'C Sharp' => 'C-Sharp',
'Crazy' => '',
'C #' => 'C-Sharp',
}
</code></pre>
<p>您可以看到值中存在大量冗余,可以通过反转哈希值来修复,这样可以很好地减少它:</p>
^{pr2}$
<p><code>'Crazy'</code>是一个离群值,但我们将处理它。在</p>
<p>Ruby的<code>String.gsub</code>有一个很好但很少使用的特性,我们可以向它传递一个正则表达式和一个哈希,它将用哈希中的等效值替换所有正则表达式匹配项。我们可以轻松构建正则表达式:</p>
<pre><code>regex = /(?:#{ Regexp.union(hash.keys).source })/
=> /(?:C\-Sharp)/
</code></pre>
<p>现在,你可能会说,“等等,我还有很多标签要找!”,而且,由于哈希的构建方式,它们隐藏在值中。为了弥补这一点,我们将反转哈希的键和值,将值数组分解为各自的元素:</p>
<pre><code>reversed_hash = Hash[hash.flat_map{ |k,v| v.map{ |i| [i,k] } }]
=> {
"C#" => "C-Sharp",
"C Sharp" => "C-Sharp",
"C #" => "C-Sharp",
}
</code></pre>
<p>通过合并“特殊情况”的第二个散列,添加<code>'Crazy'</code>很容易:</p>
<pre><code>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" => ""
}
</code></pre>
<p>将其与regex构建代码一起使用:</p>
<pre><code>regex = /(?:#{ Regexp.union(reversed_hash.keys).source })/
=> /(?:C\#|C\ Sharp|C\ \#|Crazy)/
</code></pre>
<p>它将使用自动生成的regex查找标记。如果需要区分大小写,请使用:</p>
<pre><code>regex = /(?:#{ Regexp.union(reversed_hash.keys).source })/i
</code></pre>
<p>创建一些要测试的文本:</p>
<pre><code>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"
</code></pre>
<p>测试<code>gsub</code>:</p>
<pre><code>puts text.gsub(regex, reversed_hash)
</code></pre>
<p>哪些输出:</p>
<pre><code>This is "#C-Sharp"
This is "#C-Sharp"
This is "#C-Sharp"
This is "#"
</code></pre>
<p>现在,我不太喜欢把大文件拖进内存中,因为这样做的伸缩性不好。今天的机器通常有很多GB的内存,但是我看到的文件仍然超过了机器的RAM。因此,我建议使用<code>File.read</code>来加载文件,然后使用一个<code>gsub</code>来处理它,而不是使用<code>File.foreach</code>。使用它可以更改代码。在</p>
<p>我是这样做的:</p>
<pre><code>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)
</code></pre>
<p>这将为所处理的每个文件创建一个.bak版本,因此如果出现问题,您可以进行回退,这通常是一个好的做法。在</p>
<hr/>
<p>编辑:我忘了CSV文件:</p>
<p>您可以使用CSV模块轻松地使用Ruby读取/创建一个,但是我还是选择YAML文件,因为它允许您在易于手工编辑或从文件生成的文件中轻松创建哈希布局。在</p>
<hr/>
<p>编辑:更多关于CSV,YAML和从另一个生成一个</p>
<p>以下是如何读取CSV并将其转换为建议的哈希格式:</p>
<pre><code>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
</code></pre>
<p>从之前取货:</p>
<pre><code>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"=>""}
</code></pre>
<p>要将CSV文件转换为更可编辑和更有用的文件,请使用上面的代码创建<code>hash</code>和{<cd9>},然后:</p>
<pre><code>require 'yaml'
puts ({
'hash' => hash,
'special_cases' => special_cases
}).to_yaml
</code></pre>
<p>看起来像:</p>
<pre><code> -
hash:
C-Sharp:
- C#
- C Sharp
- ! 'C #'
special_cases:
- Crazy
</code></pre>
<p>剩下的你可以从YAML文档中找到。在</p>