虽然从技术上讲这些文件是 CSV,但我们可以将 CSV 文件视为文本,因为它们就是这样。这使得它们在简单时更容易处理。
我会开始:
File.open('csv.new', 'w') do |fo|
DATA.each_line do |li|
fo.puts li.sub('123', '456')
end
end
__END__
"abc is 123 test", 1
"abc is 123 test", 2
"abc is 123 test", 3
"abc is 123 test", 4
"abc is 123 test", 5
运行它会生成一个名为“csv.new”的文件,其中包含:
"abc is 456 test", 1
"abc is 456 test", 2
"abc is 456 test", 3
"abc is 456 test", 4
"abc is 456 test", 5
代替:
DATA.each_line do |li|
您希望使用以下方式打开原始文件:
File.foreach("/home/test/files/abc.csv") do |li|
(DATA 和 __END__ 是访问存储在 Ruby 脚本末尾的示例数据的一种方式。)
'123' 容易出现误报,并且会更改子字符串:
'0123456'.sub('123', '456') # => "0456456"
为了反驳这一点,如果有任何子字符串匹配的机会,您会希望使用更智能的搜索字符串;我会使用正则表达式:
'0123456'.sub(/\b123\b/, '456') # => "0123456"
现在检查123周围是否有单词边界:
'0 123 456'.sub(/\b123\b/, '456') # => "0 456 456"
由于“123”可能会发生变化,因此将其分配给一个常量然后将其替换为模式是有意义的:
TARGET_STR = '123'
'0123456'.sub(/\b#{TARGET_STR}\b/, '456') # => "0123456"
'0 123 456'.sub(/\b#{TARGET_STR}\b/, '456') # => "0 456 456"
因为我使用带有open 和foreach 的块,Ruby 将在块结束后自动关闭文件,从而使代码更简洁,并更好地管理文件句柄。
您的代码:
file = IO.read(/home/test/files/abc.csv")
file_final = expected_file.gsub!("abc is".*, string_replace)
File.open(f1, 'w') { |f| f.write(file_final) }
……是……一团糟。
-
read 非常适合您知道大小始终低于 1MB 的文件。如果您不知道这一点,尤其是在文件可以达到 GB 范围的生产环境中工作时,使用逐行 IO 会更快、更安全,因为它可以避免可伸缩性问题。有关详细信息,请参阅“Why is "slurping" a file not a good practice?”。
- 我们不知道
expected_file 是什么,但它会导致错误,因为它是未定义的,所以 Ruby 会反抗,因为您在 nil 值上使用了 gsub! 方法。
-
如果expected_file 是一个字符串,expected_file.gsub! 会改变expected_file,但是将结果分配给file_final 会浪费CPU。而是重用expected_file,或者更好的是,使用:
file_final = expected_file.gsub(
"abc is".* 是无效参数。可能"abc is.*" 会更接近,但似乎您正在使用正则表达式/abc is.*/,但这没有必要更改字符串,/123/ 或'123' 就足够了。
-
gsub 在这里也太过分了,因为你只需要一个替换,所以sub 会更快。
-
技术上,
File.open(f1, 'w') { |f| f.write(file_final) }
会起作用,但它更容易写成
File.write(f1, file_final)
您可以将代码简化为:
File.write(
'file.csv.new',
File.read('file.csv').gsub(/\b123\b/, '456')
)
出于反常,可以写成:
File.write('file.csv.new', File.read('file.csv').gsub(/\b123\b/, '456'))
速度不会提高,反而会降低可读性。