Я бы пошел с сокращением исходных строк с помощью набора функций, изменяющих соответствующие позиции в строке.
funs =
[22, 34, 46, 55, 79, 103, 115, 139, 151, 175, 188, 200, 224, 265, 274]
|> Enum.map(& &1 - 1)
|> Enum.map(fn len ->
fn <<s :: binary-size(len), " ", rest :: binary>> ->
s <> "\t" <> rest
end
end)
input
|> String.trim
|> String.split("\n")
|> Enum.map(fn line ->
Enum.reduce(funs, line, fn fun, acc -> fun.(acc) end)
end)
Это можно сделать более элегантным способом, используя сгенерированные макросы, один раз на позицию,и рекурсивные вызовы, но сокращение списка функций выглядит для меня более простым.
Преимущество этого подхода состоит в том, что он немедленно завершается сбоем на любых противоречивых данных, гарантируя (более или менее), что если он прошел, преобразование было выполнено правильно, в отличие от всех других более коротких решений.
Кроме того, это значительно быстрее, чем любое решение Regex
.
Так как это должно применяться к 16M строкам, вот, пожалуй, наиболее производительная версия, которая соответствует всей строке сразу:
input
|> String.trim
|> String.split("\n")
|> Enum.map(
# [22, 34, 46, 55, 79, 103,
# 115, 139, 151, 175, 188,
# 200, 224, 265, 274]
# note: this assumes the listed positions above are 1-based
fn <<
c1 :: binary-size(21),
" ",
c2 :: binary-size(11),
" ",
c3 :: binary-size(11),
" ",
c4 :: binary-size(8),
" ",
c5 :: binary-size(23),
" ",
c6 :: binary-size(23),
" ",
c7 :: binary-size(11),
" ",
c8 :: binary-size(23),
" ",
c9 :: binary-size(11),
" ",
c10 :: binary-size(23),
" ",
c11 :: binary-size(12),
" ",
c12 :: binary-size(11),
" ",
c13 :: binary-size(23),
" ",
c14 :: binary-size(40),
" ",
c15 :: binary-size(8),
" ",
c16 :: binary
>> ->
c1 <> "\t" <>
c2 <> "\t" <>
c3 <> "\t" <>
c4 <> "\t" <>
c5 <> "\t" <>
c6 <> "\t" <>
c7 <> "\t" <>
c8 <> "\t" <>
c9 <> "\t" <>
c10 <> "\t" <>
c11 <> "\t" <>
c12 <> "\t" <>
c13 <> "\t" <>
c14 <> "\t" <>
c15 <> "\t" <>
c16
end)