Должен быть лучший способ заменить только отдельные символы новой строки?

27

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

Теперь у меня есть файл, написанный в этом стиле, который я хотел бы просто отправить в виде простого текста. Я хочу удалить все одиночные разрывы строк, но оставлю их без изменений. Вот что я сделал:

sed 's/$^/NEWLINE/' file.txt | awk '{printf "%s ",$0}' | sed 's/NEWLINE/\n\n/g' > linebreakfile.txt

Это заменяет пустые строки некоторым текстом, который, я уверен, не появляется в файле: NEWLINEзатем он удаляет все разрывы строк с помощью awk (я нашел этот трюк на каком-то веб-сайте), а затем заменяет NEWLINEs необходимыми двумя переносами строк. ,

Это похоже на длинный способ сделать довольно простую вещь. Есть ли более простой способ? Кроме того, если бы был способ заменить несколько пробелов (которые иногда по какой-то причине закрадывались) на отдельные пробелы, это тоже было бы хорошо.

Я использую emacs, так что если есть какой-то трюк, специфичный для emacs, это хорошо, но я бы предпочел увидеть версию sed или pure awk.

Симус
источник
Вы имели в виду ^ $, а не $ ^ в первой команде sed.
пользователь неизвестен
@ Пользователь да, да, я сделал.
Симус
Более простой способ , чтобы удалить все разрывы строк: tr -d "\n".
jfg956

Ответы:

18

Вы можете использовать awk следующим образом:

$ awk ' /^$/ { print; } /./ { printf("%s ", $0); } ' test

Или если вам нужен дополнительный перевод строки в конце:

$ awk ' /^$/ { print; } /./ { printf("%s ", $0); } END { print ""; } ' test

Или, если вы хотите отделить абзацы новой строкой:

$ awk ' /^$/ { print "\n"; } /./ { printf("%s ", $0); } END { print ""; } ' test

Эти команды awk используют действия, которые защищены шаблонами:

/regex/

или

END

Следующее действие выполняется, только если шаблон соответствует текущей строке.

И ^$.символы имеют особое значение в регулярных выражениях, где ^соответствует начало строки, $конец и .произвольный символ.

maxschlepzig
источник
Это хорошо, хотя я бы предпочел оставить пустую строку между абзацами. Я предполагаю, что вы могли бы сделать что-то вроде этого, добавив дополнительную новую строку где-нибудь в первой команде печати? Кроме того, что /./делает: кажется, что ведет себя как и elseдля /^$/совпадения строк, это правильно?
Симус
1
@Seamus, конечно - просто замените первый отпечаток (обновил ответ) - /./ соответствует всем строкам длиной не менее одного символа, то есть дополнением к шаблону / ^ $ /, который соответствует только пустым строкам.
maxschlepzig
9

Используйте режим абзаца в Awk или Perl для обработки файла абзац за абзацем, где абзацы разделены пустыми строками.

awk -vRS= '
  NR!=1 {print ""}      # print blank line before every record but the first
  {                     # do this for every record (i.e. paragraph):
    gsub(" *\n *"," "); # replace newlines by spaces, compressing spaces
    sub(" *$","");      # remove spaces at the end of the paragraph
    print
  }
'
perl -000 -pe '             # for every paragraph:
  print "\n" unless $.==1;  # print a blank line, except before the first paragraph
  s/ *\n *(?!$)/ /g;        # replace newlines by spaces, compressing spaces, but not at the end of the paragraph
  s/ *\n+\z/\n/             # normalize the last line end of the paragraph
'

Конечно, так как это не анализирует (La) TeX, он будет ужасно искажать комментарии, дословное окружение и другой специальный синтаксис. Возможно, вы захотите взглянуть на DeTeX или другие (La) TeX-текстовые конвертеры.

Жиль "ТАК - перестань быть злым"
источник
8

Sed Solution

$ sed -e ':a;N;$!ba;s/\(.\)\n/\1 /g' -e 's/\n/\n\n/' test.text

Обратите внимание, что в этом решении :aсоздается метка, а не aкоманда.

Замена нескольких пробелов

Используйте tr:$ tr -s ' ' <test.text

Стивен Д
источник
8

Если я правильно понял, то пустая строка включает в себя две последовательные переводы строк, \n\n.

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

В Perl предварительное утверждение является одним из способов достижения этого:

$ perl -0777 -i -pe 's/\n(?=[^\n])//g' test
  • -0777Флаг эффективно хлебает весь файл в одну строку
  • -p говорит Perl напечатать строку, над которой он работает по умолчанию
  • -i определяет редактирование на месте
  • Глобальное соответствие гарантирует, что все отдельные вхождения новой строки обрабатываются
Зайд
источник
Одна из проблем заключается в том, что между предложениями нет пробелов.
Стивен Д.
6

(возрождая древний вопрос)

Похоже, это именно то, что fmtи parдля переформатирования абзаца. Как и вы (а также многие программы), они определяют границы абзаца как одну (или более) пустую строку. Попробуйте передать текст через один из них.

fmt является стандартной утилитой Unix и может быть найдена в GNU Coreutils.

parэто сильно улучшенный текст, fmtнаписанный Адамом М. Костелло, который можно найти по адресу http://www.nicemice.net/par/ (он также был упакован для нескольких дистрибутивов, включая Debian - я упаковал его для Debian в январе 1996 года, хотя теперь есть новый сопровождающий для pkg.)

саз
источник
6
sed -e'/./{H;$!d;}' -e'x;s/\n//g'

sedдобавит любую строку в Hстарое пространство, которое содержит хотя бы один символ. Это сразу же означает, dчто все те, за исключением, возможно, последнего. Единственные строки, которые могут остаться, являются пробелами, и именно на этих линиях sede xизменяет пробелы удержания и образца и удаляет все накопленные \nсимволы ewline.

Если вы хотите, чтобы строки, содержащие только <табуляции> или <пробелы>, считались пустыми, замените /./адрес выше на /[^[:blank:]]/. Чтобы также сжать пробелы, сделайте:

 sed -e'/./{H;$!d;}'    \
     -e'x;s/\n//g'      \
     -e's/\([[:blank:]]\)*/\1/g'
mikeserv
источник
5

Увидев компактные примеры Жиля по perl и awk, я не захотел публиковать это, но я уже прошел это упражнение, и это действующий сценарий, который достаточно документирован; один этот момент может быть интересен для некоторых .. (соблазнительно с комментариями! :)

Этот скрипт считает пустые строки пустыми, даже если они содержат пробелы.
Несколько пробелов в тексте сжаты в один пробел.
Конечный пробел удаляется из текстовых строк. Последовательные пустые строки свернуты в одну строку. Скрипт оставляет верхние и нижние пустые строки без изменений.

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

с использованием расширенного синтаксического
вызова регулярного выражения : $ sed -rf text-file script

  :first-empty-line
  #================
  /^[[:space:]]*$/ { # if pattern-space is empty...
      $q  # last line # flush-quit 
      n   # pattern-flush=nextline-continue

      :subsequent-empty-line
      #=====================
      /^[[:space:]]*$/ { # if pattern-space is empty...
          $d        # last line # pattern-delete-cycle
          N         # pattern+=nl+nextline
          s/.*\n//  # scrap the leading 'blank' line
          t subsequent-empty-line # branch-on-substitute
      }
  }

  :text-line
  #=========
  $q                       # last line # flush-quit 
  s/^(.*)[[:space:]]*/\1/  # trim trailing whitespace
  s/ +/ /g                 # condense mulltiple spaces
  N                        # pattern+=nl+nextline
  /^.*\n[[:space:]]*$/ { # if newly-read line is blank 
      P          # pattern-first-line-print
      s/^.*\n//  # remove the leading 'text' line
      t first-empty-line   # branch-on-substitute
  }
  # read line is text
  s/\n/ /      # replace \n with a space
  t text-line  # branch-on-substitute

Примечание: flushв комментариях означает: отправить шаблонное пространство внутренней обработке stdout sed. Это не означает определенный отпечаток на стандартный вывод. Выход зависит от -nопции sed . например. qкоманда означает флеш и не бросить ... Сравните эти два фрагмента: echo x |sed -e qпечатает й, echo x |sed -ne qпечатает ничего, в то время как с помощью pкоманды напечатает «х» дважды или один раз, в зависимости от -nварианта.

Peter.O
источник
+1 за хорошие комментарии. Я видел слишком много программ без комментариев вообще.
Дэвид Кэри,
4

Вот еще одно sedрешение, которое объединяет все строки в sed«пространство удержания», так что мы получаем одну длинную строку, которая в итоге копируется в «пространство образца» для сопоставления с образцом.

Так как переводы строк будут сохраняться в последней длинной строке в sed«пространстве образца», пустые строки в терминах двойных разрывов строк [^\n]\n\n[^\n]можно сопоставлять и изменять [^\n]\n[^\n].

Для получения дополнительной информации см., Например, sed и Multi-Line Search and Replace .

text='
line 1

line 2
line 3





line 4


line     5



line 6
line 7

line 8
'

# FreeBSD sed
# first sed deletes first / last line if empty and squeezes multiple spaces
printf '%s' "$text" |
sed -e '1{/^$/d;}' -e '${/^$/d;}' -e '/[[:space:]]\{2,\}/s// /g' | 
sed -n -e '1h;1!H;${;g;/\([^[:cntrl:]]\)\n\n\([^[:cntrl:]]\)/s//\1\
\2/g;p;}' |
nl -b a


# GNU sed
# alternative using ...;x;... instead of ...;g;...
# cf. man sed | less -p '\]x'
printf '%s' "$text" |
gsed -e '1{/^$/d;}' -e '${/^$/d;}' -e '/[[:space:]]\{2,\}/s// /g' | 
gsed -E -n '1h;1!H;${;x;/([^\n])\n\n([^\n])/s//\1\
\2/g;p;}' | 
nl -b a


# remove all the single linebreaks but leave the double linebreaks intact
printf '%s' "$text" | 
   sed -n -e '1h;1!H;${;g;/\([^[:cntrl:]]\)\n\([^[:cntrl:]]\)/s//\1 \2/g;p;}' | 
   nl -b a
ДЭСО
источник
3

Это может быть старая школа:

(echo ".pl 1" ; echo ".ll 80" ; echo ".ad l" ; cat your_file) | nroff

Это выведет ваш текст выровненным по левому краю ( .ad l) с длиной строки 80 ( .ll 80). Параметр длины страницы ( .pl) указывает текстовому процессору выполнять заполнение страницы для длины страницы 1, поэтому заполнение страницы не требуется.

Если вы хотите, чтобы все ваши абзацы были в одной строке, вы можете использовать большое число для .ll:

(echo ".pl 1" ; echo ".ll 1000000" ; echo ".ad l" ; cat your_file) | nroff

man 7 groff для большего количества вариантов форматирования.

jfg956
источник
1

В Emacs я иногда использую это regex:

^J\([^^J]\) -> \1

Средства:

замените каждую новую строку, за которой следует что-то, что НЕ является новой строкой, только тем, что следует за новой строкой. Таким образом, я избавляюсь от всех новых строк внутри абзаца, но сохраняю абзацы (двойные строки)

Emacs-пользователь
источник
0

Оказывается, с auto-fill-modeвключенным, emacs делает довольно хорошую работу для моих простых сценариев использования с просто M-q...

Симус
источник
Детали того, что auto-fill-modeдействительно зависит от того, какой основной режим у вас активен.
dmckee