【问题标题】:Problem with TXT file extraction in rubyruby中TXT文件提取的问题
【发布时间】:2011-08-16 01:47:15
【问题描述】:

我有 TXT 格式的数据文件,我喜欢使用下面的 ruby​​ 代码从 TXT 文件中解析 URL 字段

f = File.open(txt_file, "r")
f.each_line { |line|
  rows = line.split(',')
  rows[3].each do |url|
    next if url=="URL"
    puts url
  end
}

TXT 包含:

name,option,price,URL
"x", "0,0,0,0,0,0", "123.40","http://domain.com/xym.jpg"
"x", "0,0,0,0,0,0", "111.34","http://domain.com/yum.jpg"

输出:

0

为什么输出来自选项字段“0,0,0,0,0,0”?如何跳过此步骤并获取 URL 字段?

环境 红宝石 1.8.7 导轨 2.3.8 宝石 1.3.7

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    我会查看一个 CSV 解析工具来简化此操作:

     require 'rubygems'
     require 'faster_csv'
    
     FasterCSV.foreach(txt_file, :quote_char => '"', 
            :col_sep =>',', :row_sep =>:auto) do |row|
       puts row[3] if row[3] != "URL"
       break
     end
    

    另外,我认为您误解了split() 的工作方式。如果您对文件中的一行运行split(),您将获得该单行的columns 数组,而不是rows[3].each 建议的多维数组。

    【讨论】:

    • 在 1.9 中是 require 'csv',因为 Faster_CSV 取代了旧的 CSV。
    【解决方案2】:

    编辑:在阅读之前,我完全同意 Jeff Swensen 的回答,无论如何我都会在这里留下答案。

    我不完全确定您的内部循环的用途 (rows[3].each) 因为当您只有一个 URL 时,您无法将单行转换为“行”。您可以按 ** 字符拆分并返回一个 url 数组,但您仍然需要删除额外的双引号,或者您可以使用正则表达式,如下所示:

    #!/usr/bin/env ruby
    
    f = DATA
    urls = f.readlines.map do |line|
      line[/([^"]+)"\*\*/, 1] 
    end
    urls.compact!
    
    p urls
    
    __END__
    name ,option,price, **URL**
    "x", "0,0,0,0,0,0", "123.40",**"http://domain.com/xym.jpg"**
    "x", "0,0,0,0,0,0", "111.34",**"http://domain.com/yum.jpg"**
    

    需要调用compact,因为map 会在您遇到与该表达式不匹配的内容时插入nil 对象。 String#[]方法见here

    【讨论】:

    • 嗨 injekt 谢谢,实际上这里的 TXT 文件充当 MySQL 转储它有 20 列,但我想解析近 8 列的值,而不是全部在 TXT 文件中。在这里,我仅在 URL 字段上发布了我面临的问题,其余字段解析完美(存储多个表关系)。
    【解决方案3】:

    结果为“0”的原因是,当您似乎期望解析 CSV 样式时,您的代码在逗号字符上盲目拆分(如果整个列值用引号括起来,则列值可能包含分隔符字符. 我强烈建议使用 csv 解析器。如果您使用的是 Ruby 1.9.2,那么您已经可以访问 FasterCSV 库。

    【讨论】:

    • 感谢buruzaemon,我正在使用ruby 1.8.7。
    【解决方案4】:

    如果你确定你想要的字段总是用双引号括起来,你可以使用它作为提取的基础而不是逗号。

    File.open(txt_file) do |f|
      f.each_line do |l|
        cols = l.scan(/(?<!\\)"(.*?)(?<!\\)"/)
        cols[3].tap{|url| puts url if url}
      end
    end
    
    • 在您的代码中,打开的 IO 并未关闭。这是一个不好的做法。最好使用块,这样您就不会忘记关闭它。
    • 正则表达式中的两个(?&lt;!\\)" 匹配非转义双引号。他们使用消极的后视。
    • .*? 是非贪婪匹配,可避免匹配超出非转义双引号。
    • tap 是为了避免在putsif 中重复两次cols[3] 操作。

    再次编辑

    如果你使用 ruby​​ 1.8.7,你可以

    • 替换正则表达式。 tap 也不能使用。请改用以下内容:

    .

    File.open(txt_file) do |f|
      f.each_line do |l|
        cols = l.scan(/(?:\A|[^\\])"(.*?[^\\]|)"/)
        url = cols[3]
        puts url if url
      end
    end
    

    我会推荐使用 oniguruma。它是自 ruby​​ 1.9 以来引入的一种新的正则表达式引擎,比 ruby​​ 1.8 中使用的引擎更强大和更快。它可以在 ruby​​ 1.8 上轻松安装。

    【讨论】:

    • 非常感谢您的回复。我错过了问题的结尾,即“f.close”。当我运行您的代码时出现错误“未定义(?...)序列:/(?
    • @prabu 我看到你添加了你使用 ruby​​ 1.8.7 的信息。我更新了我的答案。
    【解决方案5】:

    数据是 CSV 格式,但如果您只想抓取字符串中的 last 字段,那么就这样做吧:

    text =<<EOT
    name,option,price,URL
    "x", "0,0,0,0,0,0", "123.40","http://domain.com/xym.jpg"
    "x", "0,0,0,0,0,0", "111.34","http://domain.com/yum.jpg"
    EOT
    
    require 'pp'
    text.lines.map{ |l| l.split(',').last }
    

    如果你想清理双引号和尾随换行符:

    text.lines.map{ |l| l.split(',').last.gsub('"', '').chomp }
    # => ["URL", "http://domain.com/xym.jpg", "http://domain.com/yum.jpg"]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-19
      相关资源
      最近更新 更多