Использование sed, tr для исправления структуры файла

У меня есть файл, строки которого должны быть

U:<text>\tD:<text>\tA:<text>\n

где ‹ text > — текст без символов табуляции или новой строки. \t — это табуляция, а \n — символ новой строки. К сожалению, некоторые поля ‹ text > содержат символ новой строки, поэтому структура нарушается. Например вот так:

U:uuu     D:ddd     A:aaa
U:uuu     D:ddd     A:aaa
U:uu
    u    D:ddd    A:aaa
U:uuu     D:ddd     A:aaa

Здесь был символ новой строки в поле U в 3-й строке, из-за чего часть содержимого, которое должно быть в 3-й строке, теперь находится в 4-й. Как я могу исправить структуру с помощью таких инструментов, как sed или tr? Я хочу удалить те символы новой строки, которых нет в конце моей записи.

Так, например, указанный выше фиксированный файл должен выглядеть так:

U:uuu     D:ddd     A:aaa
U:uuu     D:ddd     A:aaa
U:uuu     D:ddd     A:aaa
U:uuu     D:ddd     A:aaa

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


person nikicc    schedule 18.08.2014    source источник
comment
Может ли текст A: содержать ошибочный перевод строки? Может ли какой-либо из разделов <text> содержать A:, U: или D:? Насколько велик файл для обработки? Можно ли загрузить весь файл в память (скажем, файлы размером менее 1 ГБ) или это нужно делать более поэтапно? Всегда ли будут ведущие пробелы в строках «продолжения»?   -  person Jonathan Leffler    schedule 19.08.2014
comment
На данный момент я нашел дополнительные новые строки только в U. Но если код может исправить потенциальные дополнительные новые строки в D и A, это супер, но если это не так, это тоже нормально. В тексте не должно быть букв A:U: и D:. Можно предположить, что файл может быть прочитан в оперативной памяти. На самом деле у меня есть тысячи файлов, но размер каждого отдельного файла не превышает 2 ГБ.   -  person nikicc    schedule 19.08.2014


Ответы (1)


Учитывая входные данные (сохраненные в файле data):

U:uuu     D:ddd     A:aaa1
U:uuu     D:ddd     A:aaa2
U:uu
    u     D:ddd     A:aaa3
U:uuu     D:ddd     A:aaa4
U:uuu     D:dd
              d     A:aaa5
U:uuu     D:ddd     A:aaa6

сценарий sed (сохраненный в файле sed.script):

/^U:.* D:.* A:.*/ { p; d; }
/^U:.* D:.*/ { N; s/\n *//; p; d; }
/^U:.*/ { N; s/\n *//; p; d; }

может быть запущен и выдает показанный вывод:

$ sed -f sed.script data
U:uuu     D:ddd     A:aaa1
U:uuu     D:ddd     A:aaa2
U:uuu     D:ddd     A:aaa3
U:uuu     D:ddd     A:aaa4
U:uuu     D:ddd     A:aaa5
U:uuu     D:ddd     A:aaa6
$

Первая строка сценария ищет U:, D: и A: в одной строке, предполагает, что она завершена (а не поврежденное текстовое поле A:), печатает строку и удаляет ее (что пропускает другие действия в сценарии). Вторая строка ищет только U: и D:; A: предположительно находится на следующей строке. Он добавляет следующую строку ввода, удаляет встроенную новую строку и последующие пробелы (если они есть), а затем печатает и удаляет, как и раньше. Третья строка ищет только U: и предполагает, что и D:, и A: находятся на следующей строке. Он добавляет следующую строку, удаляет встроенную новую строку и последующие пробелы (если они есть), а затем печатает и удаляет, как и раньше.

Расширение его для обработки разрывов в текстовом поле A: будет нетривиальным. Также было бы нетривиально расширить его для обработки:

U:uu
    u     D:dd
              d     A:aaa7

Ни то, ни другое формально невозможно (особенно если вы решите использовать Perl или Python вместо sed), но не совсем просто. Двойной сплит проще в обращении; внутри третьей строки у вас будет второй набор условных действий, основанный на том, найдено ли A: или нет, и т. д.

Обработка нескольких разделений для одного поля:

U:u
   u
    u
           D:d
              d
               d
                      A:aaa

также будет сложно, вероятно, выполнимо, даже в sed, но сложно.

person Jonathan Leffler    schedule 18.08.2014
comment
Это сработало отлично! Только одно замечание: при копировании скрипта могут появиться пробелы между U:.* и D:.*, которые нужно заменить табуляцией. - person nikicc; 19.08.2014
comment
Очень честный комментарий; Я тестировал со всеми заготовками, а не с вкладками. Вы можете заменить .* на [^<tab>]*, а пробел перед следующим D: или A: на табуляцию; промыть и повторить. За новой строкой может следовать [[:space:]] или около того, вместо пустой звезды. Ключ, однако, находится в остальной части скрипта, а не в мелочах регулярных выражений, обрабатывающих пробелы. - person Jonathan Leffler; 19.08.2014
comment
@Jonathan VG объяснение - person Jay Gray; 19.08.2014