Має бути кращий спосіб замінити лише окремі нові рядки?


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 або чистий awk.


Ви мали на увазі ^ $, а не $ ^ у першій команді sed.
користувач невідомий

@user так, так, я зробив.
Seamus

Ще простіше , щоб видалити всі розриви рядків: 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

Наступна дія виконується, лише якщо шаблон відповідає поточному рядку.

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


Це добре, хоча я б вважав за краще , щоб тримати порожній рядок між абзацами. Я припускаю, що ви могли зробити щось подібне, додавши додатковий новий рядок десь у першій команді друку? Крім того, що робиться /./: здається, це діє як elseдля /^$/матчу струн, чи правильно це?
Seamus

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 рішення

$ 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обирає всіх тих, що виходять, можливо, останніми. Єдині лінії , які можуть залишатися в заготовки, і саме на цих лініях , коли sedе xзмінює трюмні і малюнок простору і видаляє всі накопичені \nсимволи ewline.

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

 sed -e'/./{H;$!d;}'    \
     -e'x;s/\n//g'      \
     -e's/\([[:blank:]]\)*/\1/g'

5

Побачивши стильні приклади Джилла та дивовижні приклади, я неохоче розміщував це, але я вже пройшов цю вправу, і це функціонуючий сценарій, який досить задокументований; цей момент може зацікавити когось .. (sed з коментарями! :)

Цей сценарій вважає порожні рядки порожніми, навіть якщо вони містять пробіли.
Кілька пробілів у тексті зведені до одного простору.
Пробіл пробілів видаляється з рядків тексту. Послідовні порожні рядки згортаються до одного рядка. Сценарій залишає верхній і нижній порожні рядки недоторканими.

Що-небудь більше, ніж самі тривіальні сценарії, sed можна записати набагато легше в структурованому вигляді, як окремий файл сценарію. Ось такий приклад.

з використанням розширеного
виклику синтаксису регулярного виразів: текстовий файл сценарію $ sed -rf

  :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. Це не означає певного друку для stdout. Вихід залежить від -nваріанту sed . напр. qкоманда означає промивання та завершення ... Порівняйте ці два фрагменти: echo x |sed -e qдрукує х, echo x |sed -ne qнічого не друкує, тоді як за допомогою pкоманди надрукує «х» двічі або один раз, залежно від -nпараметра.


+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 для додаткових варіантів форматування.


1

У Emacs я інколи використовую це regex:

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

Засоби:

замініть кожен новий рядок, за яким слідує те, що НЕ є новим рядком, лише тим, що не було за новим рядком. Таким чином я позбудусь усіх нових рядків в абзаці, але зберігаю абзаци (подвійні нові рядки)


0

Виявляється, що auto-fill-modeувімкнено, Emacs виконує досить непогану роботу для моїх простих випадків використання з просто M-q...


Деталі того, що auto-fill-modeзалежить, залежать від того, який основний режим ви працюєте.
dmckee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.