text =<<~END
INVOICE # 2599
INVOICE 0185570
INVOICE: 1739
INVOICE- 45441
INVOICE:# 1234
INVOICE :# 5678
END
text.each_line.map { |s| s.gsub(/\s+(?!\d)/,'').split }
#=> [["INVOICE#", "2599"], ["INVOICE", "0185570"], ["INVOICE:", "1739"],
# ["INVOICE-", "45441"], ["INVOICE:#", "1234"], ["INVOICE:#", "5678"]]
Регулярное выражение, используемое gsub
, гласит: «соответствует одному или нескольким пробелам, за которыми не следует цифра», (?!\d)
означает негативный взгляд .Это немного отличается от s.gsub(/\s+(?=\D)/,'')
, «соответствует одному или нескольким пробелам, за которыми следует нецифровка», так как первый удаляет символ новой строки в конце каждой строки, а последний - нет.
Шагиследующим образом:
enum1 = text.each_line
#=> #<Enumerator: "INVOICE # 2599\nINVOICE 0185570\nINVOICE: 1739\n
# INVOICE- 45441\nINVOICE:# 1234\nINVOICE :#5678\n":each_line>
Я использовал String#each_line
вместо String#lines
(или другие способы создания массива строк), чтобы избежать создания временного массива.
enum2 = enum1.map
#=> #<Enumerator: #<Enumerator: "INVOICE # 2599\nINVOICE 0185570\nINVOICE: 1739\n
# INVOICE- 45441\nINVOICE:# 1234\nINVOICE :# 5678\n":each_line>:map>
s = enum2.next
#=> "INVOICE # 2599\n"
t = s.gsub(/\s+(?!\d)/,'')
#=> "INVOICE# 2599"
t.split
#=> ["INVOICE#", "2599"]
s = enum2.next
#=> "INVOICE 0185570\n"
t = s.gsub(/\s+(?!\d)/,'')
#=> "INVOICE 0185570"
t.split
#=> ["INVOICE", "0185570"]
и т. Д.
Еще один способ сделать это - удалить пробелы, за которыми не следует цифра, прежде чем строка будет разбита на строки:
text.gsub(/\s+(?!\d)/, '').each_line.map(&:split)
#=> [["INVOICE#", "2599"], ["INVOICE", "0185570"], ["INVOICE:", "1739"],
# ["INVOICE-", "45441"], ["INVOICE:#", "1234"], ["INVOICE:#", "5678"]]