【问题标题】:Is there a base strip method in Ruby?Ruby中有基本的剥离方法吗?
【发布时间】:2019-12-05 18:55:31
【问题描述】:

我有这个 sn-p 代码,我打算用它来从 CSV 文件中获取名字和姓氏,以获取特定的输出。最终目标是在用户名中包含姓氏以及名字的第一个和最后一个字母。

我拥有的 CSV 文件有名字和姓氏标题。

例子:

名字 John 姓氏:Doe 期望的输出:doejn

CSV 文件的前两行是什么样的:

First Name:        Last Name:
John               Doe

我拥有的代码:

require 'csv'

filename = 'employeedata.csv'

CSV.foreach(filename, headers: true) do |row|
   first_name_char = row['First Name'].strip.split('')
   useradd_name = "#{row['Last Name']}#{first_name_char.first}#{first_name_char.last}"
   puts useradd_name
end

期望的输出:

DoeJn

当我这样做时,它会出错。

这是我得到的错误:

Traceback (most recent call last):
        13: from Nick_Hyder_Project3.rb:17:in `<main>'
        12: from C:/Ruby26-x64/lib/ruby/2.6.0/csv.rb:509:in `foreach'
        11: from C:/Ruby26-x64/lib/ruby/2.6.0/csv.rb:657:in `open'
        10: from C:/Ruby26-x64/lib/ruby/2.6.0/csv.rb:510:in `block in foreach'
         9: from C:/Ruby26-x64/lib/ruby/2.6.0/csv.rb:1236:in `each'
         8: from C:/Ruby26-x64/lib/ruby/2.6.0/csv.rb:1236:in `each'
         7: from C:/Ruby26-x64/lib/ruby/2.6.0/csv/parser.rb:303:in `parse'
         6: from C:/Ruby26-x64/lib/ruby/2.6.0/csv/parser.rb:779:in `parse_quotable_loose'
         5: from C:/Ruby26-x64/lib/ruby/2.6.0/csv/parser.rb:28:in `each_line'
         4: from C:/Ruby26-x64/lib/ruby/2.6.0/csv/parser.rb:28:in `each_line'
         3: from C:/Ruby26-x64/lib/ruby/2.6.0/csv/parser.rb:31:in `block in each_line'
         2: from C:/Ruby26-x64/lib/ruby/2.6.0/csv/parser.rb:827:in `block in parse_quotable_loose'
         1: from C:/Ruby26-x64/lib/ruby/2.6.0/csv/parser.rb:1078:in `emit_row'
Nick_Hyder_Project3.rb:18:in `block in <main>': undefined method `strip' for nil:NilClass (NoMethodError)

是我没有正确的库引起的错误吗?它会因为我没有数组来存储新用户名而出错吗?

【问题讨论】:

  • 希望编辑能更清楚地显示问题。

标签: ruby csv parsing


【解决方案1】:

让我们先构建文件。

str =<<~END
First Name:        Last Name:
John               Doe
Mary               Smith
END

FName = 't.csv'
File.write(FName, str)
  #=> 78

让我们确认文件内容。

puts File.read(FName)
First Name:        Last Name:
John               Doe
Mary               Smith

困难在于您有一个用于标题的字段分隔符(冒号后跟可变数量的空格)和用于正文的每一行的另一个字段分隔符(仅空格)。这不是有效的 CSV 文件格式。因此,最好仅将其视为普通文本文件,不要尝试使用 CSV 方法。

first_line, *rest = File.readlines(FName)
  #=> ["First Name:        Last Name:\n",
  #    "John               Doe\n",
  #    "Mary               Smith\n"]

这会产生以下结果:

first_line
  #=> "First Name:        Last Name:\n"
rest
  #=> ["John               Doe\n",
  #    "Mary               Smith\n"]

现在将rest 转换为更方便的形式:

arr = rest.map { |line| line.scan(/\S+/) }
  #=> [["John", "Doe"], ["Mary", "Smith"]]

String#scan。正则表达式读取,“匹配一个或多个不是空格的字符”。空格和换行符 ("\n") 都是空格。

如果我们知道文件每一行中的名字在姓氏之前,我们就可以继续获得所需的结果。但是,如果我们不知道这一点,我们可以执行以下操作:

arr.map!(&:reverse) if first_line.start_with?("Last Name:")

如果第一行改为 "Last Name: First Name:\n" 并且正文的每一行中的两个名称颠倒过来,这会将 arr 转换为所需的形式:

[["John", "Doe"], ["Mary", "Smith"]]

最后,我们只需要将arr 转换为想要的结果:

arr.map { |first_name, last_name| last_name + first_name[0] +
  first_name[-1] }
  #=> ["DoeJn", "SmithMy"]

如果名字可以由单个字符组成或者一个人可能没有名字(例如,“Cher”),这当然必须进行调整。

注意arr.map!(&amp;:reverse) 实际上是arr.map! { |a| a.reverse } 的简写。

方法IO::readlines1gulps 将文件放入一个数组中。但是,如果文件足够大,则可能没有足够的可用内存来执行此操作。在这种情况下,必须逐行读取文件。一种方法如下。

first_line_read = false
File.foreach(FName).with_object([]) do |line, arr|
  if first_line_read == false
    first_name_first = line.start_with?("First Name:")       
    first_line_read = true
    next
  end
  a = line.scan(/\S+/)
  a.reverse! unless first_name_first
  arr << (a.last + a.first[0] + a.first[-1])
end
  #=> ["JohnDe", "MarySh"] 

1.以File 作为接收者来调用IO 方法是常见的做法。这是允许的,因为FileIO 的子类。

【讨论】:

    【解决方案2】:

    出现错误是因为 row['First Name'] 正在评估为 nil。请正确检查您的列名,并确保正确评估 row['First Name'] 的值。

    您可能需要转义 row['First Name'] 中的 space

    【讨论】:

      猜你喜欢
      • 2016-10-03
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      • 2020-12-01
      • 2015-01-20
      • 2013-01-15
      • 2011-11-16
      • 2017-02-01
      相关资源
      最近更新 更多