Проблема с извлечением файла TXT в ruby - PullRequest
0 голосов
/ 30 апреля 2011

У меня есть файл данных в формате TXT, мне нравится анализировать поле URL из файла TXT, используя указанный ниже код рубина

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

Ответы [ 5 ]

2 голосов
/ 30 апреля 2011

Я бы воспользовался инструментом разбора 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 голос
/ 30 апреля 2011

Если вы уверены, что поля, которые вы хотите, всегда заключены в двойные кавычки, вы можете использовать их как основу для извлечения, а не запятую.

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 не закрыт. Это плохая практика. Лучше использовать блок, чтобы не забыть его закрыть.
  • Два (?<!\\)" в регулярном выражении соответствуют не экранированным двойным кавычкам. Они используют отрицательный взгляд назад.
  • .*? - это не жадное совпадение, которое не позволяет совпадению превышать неэкранированную двойную кавычку.
  • tap позволяет избежать повторения операции cols[3] дважды в puts и if.

Изменить еще раз

Если вы используете ruby ​​1.8.7, вы можете либо

  • обновите свой движок регулярных выражений до oniguruma, выполнив простые шаги здесь, http://oniguruma.rubyforge.org/

или

  • заменить регулярное выражение. 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

Я бы рекомендовал использовать онигурума. Это новый движок регулярных выражений, представленный начиная с ruby ​​1.9, и он намного мощнее и быстрее, чем тот, что использовался в ruby ​​1.8. Его можно легко установить на ruby ​​1.8.

1 голос
/ 30 апреля 2011

Причина, по которой "0" является результатом, заключается в том, что ваш код слепо разделяется на запятую, когда вы, похоже, ожидаете синтаксический анализ в стиле CSV (где значения столбцов могут содержать символы разделителя, если все значение столбца заключено в кавычкиЯ настоятельно рекомендую использовать парсер csv. Если вы используете Ruby 1.9.2, то у вас уже будет доступ к библиотеке FasterCSV .

1 голос
/ 30 апреля 2011

РЕДАКТИРОВАТЬ : Перед прочтением я полностью согласен с ответом Джеффа Свенсена, я оставлю здесь свой ответ независимо от этого.

Я не совсем уверен, каков ваш внутренний циклfor (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#[] см. здесь

0 голосов
/ 01 мая 2011

Данные представлены в формате 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"]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...