Ruby пользовательская функция разделения медленно - PullRequest
0 голосов
/ 22 октября 2018

У меня большой файл данных, разделенных в основном пробелами, которые я хочу проанализировать в хеш.Проблема в том, что это в основном , разделенный пробелами, поэтому простой string.split не сработает.

Вот упрощенный пример одной из строк в файле:

field0 field1 [ [field2a] [field2b] ] field3

Содержимое внешних скобок (включая внешние скобки) должно быть элементом хеш-функции.

Я написал следующую функцию, которая работает, но очень медленно:

# row = String to be split
# fields = Integer indicating expected number of fields
def mysplit (row, fields)

 # Variable to keep track of brackets
 b = 0

 # Variable to keep track of iterations for array index
 i = 0

 rowsplit = Array.new(fields)
 rowsplit[0] = ""
 row.each_char do |byte|

  case byte

   when ' '
    if b == 0
     i += 1
     rowsplit[i] = ""
    else
     rowsplit[i] += byte
    end

   when '['
    b += 1
    rowsplit[i] += byte

   when ']'
    b -= 1
    rowsplit[i] += byte

   else
    rowsplit[i] += byte

  end

 end

 if i != fields - 1
  raise StandardError,
   "Resulting fields do not match expected fields: #{rowsplit}",
   caller
 elsif b != 0
  raise StandardError, "Bracket never closed.", caller
 else
  return rowsplit
 end

end

Требуется 36 секунд для запуска этого файла размером 7 МБ длиной 6600 строк.Стоит отметить, что в моей среде работает Ruby 1.8.7, который я не контролирую.

Можно ли сделать это быстрее?

Ответы [ 2 ]

0 голосов
/ 23 октября 2018

Чтобы действительно настроить свой код, вы можете использовать модуль тестов, чтобы найти узкое место.

Но я ожидаю, что самой большой проблемой вашего кода является добавление строки:

rowsplit[i] += byte

интерпретатор ruby ​​переводит это в

rowsplit[i] = rowsplit[i] + byte

Это создает новый строковый объект для каждого байта в вашем входном файле.Таким образом, 7-мегабайтный файл создает и уничтожает семь миллионов строковых объектов ... Вы, вероятно, достаточно быстро используете метод конкатенации строк:

rowsplit[i] << byte

Остерегайтесь того, что << меняет исходный объект, это непроблема в вашей программе, но может быть проблемой, когда вы используете ее в других контекстах.

0 голосов
/ 22 октября 2018

Вы хотите .squeeze и .strip

str = "  field0     field1 [ [field2a] [field2b] ] field3"

puts str.squeeze.strip
#=> "field0 field1 [ [field2a] [field2b] ] field3"

Сжатие сжимает любой лишний пробел до единицы. Полоска удалит начальное и конечное пространство строки.

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

Вам также следует попробоватьчтобы повысить ожидания раньше, не нужно перебирать весь файл.

Если вы знаете, что ваша линия будет соответствовать этому шаблону в вашем примере:

if str.squeeze!.strip! !str[/\w+\ +\[\ +\[+\w+\]\ \[+\w+\]\ \]\ \w+/]
  raise StandardError, "Raise this string pattern is wrong #{str}"
end

Если вы хорошо, то вы можете разделить или что-то еще:

str.split(' ')
#=>["field0", "field1", "[", "[field2a]", "[field2b]", "]", "field3"] 
...