迭代具有多个全大写名称的 CSV 并将它们更改为正确的大写
Iterating through a CSV that has a number of all-caps names and changing them to proper capitalization
我有一大堆 table 的名字,其中一些全大写。我想遍历 table 并将全部大写的名称变成常规大写的名称。我有几个问题:
- 我很确定我没有正确地将 CSV 转换为数组以供 Ruby 处理
- 当我使用一些虚拟数据作为我放入 .each 循环的数组时,我的 .downcase!.capitalize!似乎适用于 |word|虽然 .each 循环在数组上 运行 但数组末尾不包含固定词
这是我尝试将我拥有的 CSV 文件转换为 Ruby 数组:
require "csv"
puts "What is the name of the CSV you want to clean?"
print ">> "
csv_name = $stdin.gets.chomp
array = CSV.parse(File.open(csv_name, "r+"))
这是虚拟数据数组,带有 .each 循环,它应该更正数组中每个全大写条目的大写,并且似乎这样做了,只是它没有坚持:
array = [["Thing", "Whatsit", "Object", "Foo", "Bar", "Baz", "Let", "Get", "Met"],
[1, 2, 3, "NULL", 5, 6, "Stuff", "test", "CRAZY"],
["THING", "LADY", "TERROR OF THE NIGHT", "MIGHTY", "nothing", "SYCOPHANT", "NEXT", "WILD", "WIFE"]]
array[0..(array.count - 1)].flatten.each do |word|
if word.to_s == word
if word.upcase == word
ary = word.split
ary[0..(ary.count-1)].each do |w|
w.downcase!.capitalize!
end
word = ary.join(" ")
puts word
word
else
word
end
word
else
word
end
end
print array
如有任何建议,我们将不胜感激!
我想这可能是您要找的:
array = array.flatten.map{|c| c.is_a?(String) ? c.split.map(&:capitalize).join(' ') : c}
你可以在解析CSV的时候使用自定义转换器来处理进来的数据。CSV库自带了一套标准的转换器,可以帮助解析整数、日期等,但是你可以自由定义定制的。
require 'csv'
CSV::Converters[:fixcaps] = ->(v) { v.capitalize rescue v }
CSV.parse(File.read(csv_name), converters: :fixcaps)
=> [["Header1", "Header2", "Header3"], ["Thing", "1", "Thing"], ["Whatsit", "2", "Lady"], ["Object", "3", "Terror of the night"], ...]
(我把测试数据精简了一些,所以和你给的不完全吻合。)
客户转换器简单地进行小写和大写。在出现错误的情况下(主要是因为字段是 nil
,转换器只是 return 原始数据作为后备。
编辑
我错过了问题的一部分,其中只有 all-caps 个单词需要修复。有几种方法可以通过正则表达式实现这一点,但这里是一个:
CSV::Converters[:fixcaps] = ->(field) do
field.gsub(/(?<!\S)[[:upper:]]+(?!\S)/) { |w| w.capitalize }
rescue
field
end
此外,在您的一条评论中,您提到真实数据有 headers,最终您希望 re-write 作为 CSV。你可以很容易地处理这两个问题:
require 'csv'
CSV::Converters[:fixcaps] = ->(v) { v.capitalize rescue v }
data = CSV.parse(File.read(csv_name), converters: :fixcaps, headers: true)
File.write('output.csv', data.to_csv)
在这种情况下,指定 headers: true
会导致 CSV.parse
到 return 一个 CSV::Table
object 而不是原始嵌套数组(在 运行 东西通过我们的自定义转换器)。 CSV::Table#to_csv
然后以CSV格式写出数据,headers先。
如果需要,您始终可以在 CSV::Table
上调用 .to_a
以获得等效的嵌套数组,但如果您有其他处理要做,Table
提供了用于操作的工具数据(例如,在 row/column 方向之间切换,只处理某些列等)比只处理嵌套数组更容易理解。
创建 CSV 文件
假设我们希望读取一个 CSV 文件,其内容由 str
:
的内容给出
str =<<~END
Fld1,Fld2,Fld3,Fld4,Fld5
Thing A MA-Jig,Whatsit,Object,FoO AND 1 GOO,Bar
1,NULL,Stuff,test,CRAZY
LADY,TERROR OF THE NIGHT,nothing,LEFT,nEXT
END
File.write('in.csv', str)
#=> 140
让我们确认它是否正确写入。
puts File.read('in.csv')
Fld1,Fld2,Fld3,Fld4,Fld5
Thing A MA-Jig,Whatsit,Object,FoO AND 1 GOO,Bar
1,NULL,Stuff,test,CRAZY
LADY,TERROR OF THE NIGHT,nothing,LEFT,nEXT
读取 CSV 文件
这里的技巧是定义一个 自定义转换器 来对具有 属性 的所有字符串执行转换,即字符串中包含的所有字母均为大写。题目要求这些字符串全部大写
自定义转换器是在读取文件时转换某些字段内容的过程。 Here 是自定义转换器的一些示例。我们想要的自定义转换器可以这样写
convert_all_caps = ->(value) do
value.gsub(/\S+/) do |word|
word == word.upcase ? word.capitalize : word
end
end
#=> #<Proc:0x0000583f969bdf88@(irb):467 (lambda)>
正则表达式/\S/
匹配除空格以外的一个或多个字符。
请注意,lambda 有一个可选的第二个参数,它是传递值的 header(例如,"Fld2"
)的名称。如果需要,这允许使用 case 语句以不同的方式转换不同的字段。
这里有两个使用这个 lambda 的例子。
convert_all_caps.call('Thing A MA-Jig')
#=> "Thing a MA-Jig"
convert_all_caps.call('FoO AND 1 GOO')
#=> "FoO And 1 Goo"
如果我们使用此自定义转换器读取我们的 CSV 文件,我们将获得以下内容。
csv = CSV.read('in.csv', :headers => true, converters: [convert_all_caps])
#=> #<CSV::Table mode:col_or_row row_count:4>
csv.to_a
#=> [["Fld1", "Fld2", "Fld3", "Fld4", "Fld5"],
# ["Thing A MA-Jig", "Whatsit", "Object", "FoO And 1 Goo", "Bar"],
# ["1", "Null", "Stuff", "test", "Crazy"],
# ["Lady", "Terror Of The Night", "nothing", "Left", "nEXT"]]
写一个 CSV 文件
如果我们希望将这些转换后的值写入另一个 CSV 文件,最有效的方法是读取输入文件 line-by-line 并将每一行写入输出文件。我们可以这样做。
headers = CSV.open('in.csv') { |csv| csv.first }
#=> ["Fld1", "Fld2", "Fld3", "Fld4", "Fld5"]
CSV.open('out.csv', "wb") do |csv_out|
csv_out << headers
CSV.foreach('in.csv', :headers => true,
converters: [convert_all_caps]) { |csv_in| csv_out << csv_in }
end
#=> 140
参见 CSV::open and CSV::foreach。
让我们确认输出文件是否正确写入。
puts File.read('out.csv')
Fld1,Fld2,Fld3,Fld4,Fld5
Thing A MA-Jig,Whatsit,Object,FoO And 1 Goo,Bar
1,Null,Stuff,test,Crazy
Lady,Terror Of The Night,nothing,Left,nEXT
我有一大堆 table 的名字,其中一些全大写。我想遍历 table 并将全部大写的名称变成常规大写的名称。我有几个问题:
- 我很确定我没有正确地将 CSV 转换为数组以供 Ruby 处理
- 当我使用一些虚拟数据作为我放入 .each 循环的数组时,我的 .downcase!.capitalize!似乎适用于 |word|虽然 .each 循环在数组上 运行 但数组末尾不包含固定词
这是我尝试将我拥有的 CSV 文件转换为 Ruby 数组:
require "csv"
puts "What is the name of the CSV you want to clean?"
print ">> "
csv_name = $stdin.gets.chomp
array = CSV.parse(File.open(csv_name, "r+"))
这是虚拟数据数组,带有 .each 循环,它应该更正数组中每个全大写条目的大写,并且似乎这样做了,只是它没有坚持:
array = [["Thing", "Whatsit", "Object", "Foo", "Bar", "Baz", "Let", "Get", "Met"],
[1, 2, 3, "NULL", 5, 6, "Stuff", "test", "CRAZY"],
["THING", "LADY", "TERROR OF THE NIGHT", "MIGHTY", "nothing", "SYCOPHANT", "NEXT", "WILD", "WIFE"]]
array[0..(array.count - 1)].flatten.each do |word|
if word.to_s == word
if word.upcase == word
ary = word.split
ary[0..(ary.count-1)].each do |w|
w.downcase!.capitalize!
end
word = ary.join(" ")
puts word
word
else
word
end
word
else
word
end
end
print array
如有任何建议,我们将不胜感激!
我想这可能是您要找的:
array = array.flatten.map{|c| c.is_a?(String) ? c.split.map(&:capitalize).join(' ') : c}
你可以在解析CSV的时候使用自定义转换器来处理进来的数据。CSV库自带了一套标准的转换器,可以帮助解析整数、日期等,但是你可以自由定义定制的。
require 'csv'
CSV::Converters[:fixcaps] = ->(v) { v.capitalize rescue v }
CSV.parse(File.read(csv_name), converters: :fixcaps)
=> [["Header1", "Header2", "Header3"], ["Thing", "1", "Thing"], ["Whatsit", "2", "Lady"], ["Object", "3", "Terror of the night"], ...]
(我把测试数据精简了一些,所以和你给的不完全吻合。)
客户转换器简单地进行小写和大写。在出现错误的情况下(主要是因为字段是 nil
,转换器只是 return 原始数据作为后备。
编辑
我错过了问题的一部分,其中只有 all-caps 个单词需要修复。有几种方法可以通过正则表达式实现这一点,但这里是一个:
CSV::Converters[:fixcaps] = ->(field) do
field.gsub(/(?<!\S)[[:upper:]]+(?!\S)/) { |w| w.capitalize }
rescue
field
end
此外,在您的一条评论中,您提到真实数据有 headers,最终您希望 re-write 作为 CSV。你可以很容易地处理这两个问题:
require 'csv'
CSV::Converters[:fixcaps] = ->(v) { v.capitalize rescue v }
data = CSV.parse(File.read(csv_name), converters: :fixcaps, headers: true)
File.write('output.csv', data.to_csv)
在这种情况下,指定 headers: true
会导致 CSV.parse
到 return 一个 CSV::Table
object 而不是原始嵌套数组(在 运行 东西通过我们的自定义转换器)。 CSV::Table#to_csv
然后以CSV格式写出数据,headers先。
如果需要,您始终可以在 CSV::Table
上调用 .to_a
以获得等效的嵌套数组,但如果您有其他处理要做,Table
提供了用于操作的工具数据(例如,在 row/column 方向之间切换,只处理某些列等)比只处理嵌套数组更容易理解。
创建 CSV 文件
假设我们希望读取一个 CSV 文件,其内容由 str
:
str =<<~END
Fld1,Fld2,Fld3,Fld4,Fld5
Thing A MA-Jig,Whatsit,Object,FoO AND 1 GOO,Bar
1,NULL,Stuff,test,CRAZY
LADY,TERROR OF THE NIGHT,nothing,LEFT,nEXT
END
File.write('in.csv', str)
#=> 140
让我们确认它是否正确写入。
puts File.read('in.csv')
Fld1,Fld2,Fld3,Fld4,Fld5
Thing A MA-Jig,Whatsit,Object,FoO AND 1 GOO,Bar
1,NULL,Stuff,test,CRAZY
LADY,TERROR OF THE NIGHT,nothing,LEFT,nEXT
读取 CSV 文件
这里的技巧是定义一个 自定义转换器 来对具有 属性 的所有字符串执行转换,即字符串中包含的所有字母均为大写。题目要求这些字符串全部大写
自定义转换器是在读取文件时转换某些字段内容的过程。 Here 是自定义转换器的一些示例。我们想要的自定义转换器可以这样写
convert_all_caps = ->(value) do
value.gsub(/\S+/) do |word|
word == word.upcase ? word.capitalize : word
end
end
#=> #<Proc:0x0000583f969bdf88@(irb):467 (lambda)>
正则表达式/\S/
匹配除空格以外的一个或多个字符。
请注意,lambda 有一个可选的第二个参数,它是传递值的 header(例如,"Fld2"
)的名称。如果需要,这允许使用 case 语句以不同的方式转换不同的字段。
这里有两个使用这个 lambda 的例子。
convert_all_caps.call('Thing A MA-Jig')
#=> "Thing a MA-Jig"
convert_all_caps.call('FoO AND 1 GOO')
#=> "FoO And 1 Goo"
如果我们使用此自定义转换器读取我们的 CSV 文件,我们将获得以下内容。
csv = CSV.read('in.csv', :headers => true, converters: [convert_all_caps])
#=> #<CSV::Table mode:col_or_row row_count:4>
csv.to_a
#=> [["Fld1", "Fld2", "Fld3", "Fld4", "Fld5"],
# ["Thing A MA-Jig", "Whatsit", "Object", "FoO And 1 Goo", "Bar"],
# ["1", "Null", "Stuff", "test", "Crazy"],
# ["Lady", "Terror Of The Night", "nothing", "Left", "nEXT"]]
写一个 CSV 文件
如果我们希望将这些转换后的值写入另一个 CSV 文件,最有效的方法是读取输入文件 line-by-line 并将每一行写入输出文件。我们可以这样做。
headers = CSV.open('in.csv') { |csv| csv.first }
#=> ["Fld1", "Fld2", "Fld3", "Fld4", "Fld5"]
CSV.open('out.csv', "wb") do |csv_out|
csv_out << headers
CSV.foreach('in.csv', :headers => true,
converters: [convert_all_caps]) { |csv_in| csv_out << csv_in }
end
#=> 140
参见 CSV::open and CSV::foreach。
让我们确认输出文件是否正确写入。
puts File.read('out.csv')
Fld1,Fld2,Fld3,Fld4,Fld5
Thing A MA-Jig,Whatsit,Object,FoO And 1 Goo,Bar
1,Null,Stuff,test,Crazy
Lady,Terror Of The Night,nothing,Left,nEXT