2

У меня есть несколько больших файлов с некоторыми измерениями.

Это выглядит так:

N 12344;PE 9.9999999;...
#S 0 0 31 44 75 130 165 196...
#S_+ "2 5 2 3 3 1 1 2 3 1 2 2...

N 12345;PE 9.9999999;...
#S 0 0 34 57 84 133 152...
#S_+ "1 0 1 1 2 3 0 0 0...

N 12346;PE 9.9999999;...
#S 0 0 31 44 73 140 169...
#S_+ "3 3 4 0 0 2 1 2 4...

N 25104;PE 9.9999999;...
#S 0 0 36 52 102 108 145...
#S_+ "1 1 0 1 0 0 3 0 1...

N 25105;PE 9.9999999;...
#S 0 0 32 58 88 130 143...

Образец здесь:http://pasted.co/d9806b7c4

Файл намного больше, но я заменил часть данных на «...», чтобы сделать его короче.

Мне нужно как-то заменить концы строки перед "#S" - фактически просто объединить строку "N" со следующими двумя в одну строку (или со следующими тремя, чтобы я мог избавиться от пустых строк). Ожидайте вывод, как это:

N 12344;PE 9.9999999; #S 0 0 31 44 75 130 165 196 #S_+ "2 5 2 3 3 1 1 2 3 1 2 2...
N 12345;PE 9.9999999; #S 0 0 34 57 84 133 152 #S_+ "1 0 1 1 2 3 0 0 0...
N 12346;PE 9.9999999; #S 0 0 31 44 73 140 169 #S_+ "3 3 4 0 0 2 1 2 4...
N 25104;PE 9.9999999; #S 0 0 36 52 102 108 145 #S_+ "1 1 0 1 0 0 3 0 1...
N 25105;PE 9.9999999; #S 0 0 32 58 88 130 143...

Можно ли добиться этого с помощью какой-либо утилиты командной строки в Linux?

Мои знания в этой области весьма ограничены, поэтому я был бы признателен за любую помощь.

Спасибо

6 ответов6

4

С помощью sed:

sed -z -e 's/\n#S/ #S/g' -e 's/\nN /N /g' data

В замедленном режиме:

  • -z заставляет sed рассматривать файл как одну строку (поэтому строки заканчиваются простыми символами)
  • 's/\n#S/#S/g' заменяет все LF, встречающиеся непосредственно перед #S , пробелом
  • -e 's/\nN /N /g' заменяет все LF перед N (т. е. пустые строки)
4

С paste (для этого необходимо всегда иметь группы из 4 строк):

 paste -s -d '   \n' data

В замедленном режиме:

  • paste -s объединяет строки из файла
  • -d указывает символы для вставки в качестве разделителей. Когда есть несколько символов, они используются в циклическом режиме, то есть с 3 пробелами и LF:
    • первый пробел используется в первом соединении (от N до #S),
    • второй пробел используется во втором соединении (от #S до #S),
    • третье место используется в тройном соединении (от #S до пустой строки),
    • последний разделитель, LF, используется в четвертом соединении (пустая строка до N)
    • и цикл повторяется для следующих 4 строк.
4

Это портативное решение с POSIX sed, реализующее следующие правила:

  • пустые строки должны быть удалены;
  • любая строка, начинающаяся с #S должна быть объединена с предыдущей непустой строкой, с одним пробелом между ними, если нет предыдущей непустой строки.

Код:

<data sed '/^$/ d; :start; N; s/\n$//; t start; s/\n#S/ #S/; t start; P; D'

То же самое с комментариями (все еще рабочий код):

<data sed '
  /^$/ d      # If empty line read, delete it and start a new cycle.
  :start      # A label.
  N           # Read additional line, there are now two lines in the pattern space.
  s/\n$//     # If the second line is empty, replace the newline with nothing.
  t start     # If the above replacement occurred, go to start (to add another line).
              # Otherwise
  s/\n#S/ #S/ # if the second line starts with #S, replace the newline with space.
  t start     # If the above replacement occurred, go to start (to add another line).
              # Otherwise
              # (i.e when non-empty line not starting with #S occurred)
  P           # print the pattern space up to the first newline and...
  D           # delete the initial segment of the pattern space
              # through the first newline (i.e. everything just printed),
              # and start the next cycle with the resultant pattern space
              # and without reading any new input
              # (in our case the new input will be explicitly read by N then).
  '

Обратите внимание, что решение использует пространство шаблонов sed для накопления множества входных строк. Это замечание относится:

Пространство шаблона и места хранения должно содержать не менее 8192 байтов.

Непосредственно перед командой P шаблонное пространство содержит одну (относительно длинную) строку, предназначенную для печати, и одну (относительно короткую) строку ввода, а также новую строку между ними. Очевидно, это зависит от ваших данных, превышает ли такая структура 8192 байта в какой-то момент. Если это произойдет, некоторые реализации sed могут завершиться ошибкой.

3

Использование Perl:

perl -0 -ape 's/\R(?=\RN|#)/ /g' file.txt
N 12344;PE 9.9999999;... #S 0 0 31 44 75 130 165 196... #S_+ "2 5 2 3 3 1 1 2 3 1 2 2...
N 12345;PE 9.9999999;... #S 0 0 34 57 84 133 152... #S_+ "1 0 1 1 2 3 0 0 0...
N 12346;PE 9.9999999;... #S 0 0 31 44 73 140 169... #S_+ "3 3 4 0 0 2 1 2 4...
N 25104;PE 9.9999999;... #S 0 0 36 52 102 108 145... #S_+ "1 1 0 1 0 0 3 0 1...
N 25105;PE 9.9999999;... #S 0 0 32 58 88 130 143...

Regex объяснить:

s/              : substitute
    \R          : any kind of line break (ie. \r, \n, \r\n)
    (?=         : positive lookahead, zero-length assertion that make sure we have after
        \RN     : a line break followed by letter N
      |         : OR
        #       : # character
    )           : end lookahead
/ /g            : replace with a space, global
3

awk (gawk [ 1 ])

Как обычно, кроме sed вы можете использовать awk (и разными способами ...)

awk 'ORS=" "; NR % 4 == 0 && ORS="\n" ' data

где

  • ORS=" " исправляет разделитель выходной записи, по умолчанию символ новой строки, в пробел (вы можете изменить)
  • NR % 4 == 0 && ORS="\n" каждую четвертую строку, которую она исправляет обратно на новую строку \n
  • Если ничего не указано, awk печатает полную строку
  • data ваш файл данных.

Если вы хотите, вы можете использовать регулярные выражения, как в sed (аналогичным образом).


Версия для проверки формата с awk

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

awk '{a=$0; getline b; getline c; 
     if ( getline > 0 ) {print a, b, c, $0 } 
     else { print "Ohi " > "/dev/stderr" ; exit 65; }  }' data

где

  • a=$0; ставит полную строку в переменной a
  • getline b; читает строку и помещает переменную b
  • getline c; неясная непостижимая команда :-)
  • if (getline) если он может прочитать строку ...
  • .............. {print a, b, c, $0} печатает 4 строки
  • else печатает ошибку на устройстве stderr (экране или другом), который вы можете настроить здесь ...
  • exit 65 возвращает код выхода, отличный от 0 --->error

Бонус: почему 65?

В поисках подходящего значения для вашего кода выхода [ 2 ] вы можете обнаружить, что его рекомендуется увидеть в /usr/include/sysexits.h среди некоторых стандартов C ...

  #define EX_DATAERR      65      /* data format error */

65 является наиболее подходящим для ошибки формата данных ...

Честно говоря, в качестве ответа я предпочел 42,
но каждое значение, отличное от нуля (и не зарезервированное [ 2 ]), может быть хорошим, а 65 является конкретным ...

0

Вы можете сделать это с помощью любого текстового редактора, который поддерживает регулярные выражения, такие как Notepad++.

Новая строка - это просто непечатаемый символ или два символа. В Windows обычно CarrigeReturn и LineFeed, а в Unix-системах обычно только LineFeed.

Чтобы увидеть их, вам нужно включить показ непечатного символа (обычно это значок абзаца). Смотрите здесь: https://imgur.com/cqiTvrp

Теперь вам нужно использовать заменитель регулярного выражения (CTRL + H) для замены CRLF # S на #S. Символом для CR является \r, а для LF - \n. Таким образом, вы получите \r \n # S или \n # S для #S. https://imgur.com/GoeVn70

Или вы можете заменить его на пробел, если вам нужно.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .