【问题标题】:Replace a string in csv using regex using ruby使用 ruby​​ 使用正则表达式替换 csv 中的字符串
【发布时间】:2017-05-24 17:59:50
【问题描述】:

我有一个 csv 文件,其中有一个列 test 和 id 和值是:

"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

我想将"abc is 123 test" 替换为"abc is 567 test"

注意:值 123567 是动态值,每个新的 csv 123 都会更改,但字符串 "abc is <value> test" 始终保持不变。

我试过的代码:

folder_path = "/home/test/files/"
f1 = folder_path + "abc.csv"
string_replace = "abc is 567 test"

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) }

我收到错误:

"ArgumentError: 调用 * 的参数数量错误(0 代表 1)

谁能帮忙?

【问题讨论】:

  • 可以多于3位,也可以少于3位。
  • “abc is 123 test”中的数字都是三位数吗?
  • 使用read 和正则表达式是个坏主意,除非您总是知道该文件很容易放入内存中。与使用逐行 IO 相比,任何超过 2MB 的内容都会减慢您的代码速度。 stackoverflow.com/questions/25189262

标签: ruby string csv


【解决方案1】:

虽然从技术上讲这些文件是 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"

因为我使用带有openforeach 的块,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'))

速度不会提高,反而会降低可读性。

【讨论】:

    猜你喜欢
    • 2015-02-27
    • 1970-01-01
    • 2015-05-21
    • 2012-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多