是否必须有更好的方法来仅替换单个换行符?


27

我习惯每句只写一行,因为我通常将内容编译为LaTex,或者以其他格式书写而忽略了换行符。我使用空白行指示新段落的开始。

现在,我有一个以这种风格编写的文件,我想将其作为纯文本发送。我想删除所有单个换行符,但完整保留两个换行符。这是我所做的:

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

这用一些我相信不会出现在文件中的文本替换空行:NEWLINE然后用awk消除了所有换行符(我在某些网站上发现了这个技巧),然后NEWLINE用必需的两个换行符替换了s 。

这似乎是完成一件非常简单的事情的漫长方法。有没有更简单的方法?另外,如果有一种方法可以用单个空格替换多个空格(有时由于某种原因有时会蔓延),那也很好。

我使用emacs,所以如果有一些emacs特定的技巧很好,但我宁愿看到一个纯sed或纯awk版本。


您的意思是^ $,而不是第一个sed命令中的$ ^。
用户未知

@user是的,是的,我做到了。
Seamus

删除所有换行符的更简单方法:tr -d "\n"
jfg956

Answers:


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 2011年

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 指定就地编辑
  • 全局匹配可确保处理所有单个换行符

这样做的一个问题是句子之间没有空格。
史蒂文D

6

(复兴一个古老的问题)

这似乎是正确的fmtpar适用于-段落重新格式化。像您(以及许多程序)一样,它们将段落边界定义为一个(或多个)空白行。尝试通过其中之一来传递文本。

fmt 是标准的UNIX实用程序,可以在GNU Coreutils中找到。

parfmtAdam M. Costello撰写的功能得到很大增强的书,该书可以在http://www.nicemice.net/par/上找到(它也已打包用于多个发行版,包括debian-我在1996年1月将其打包为debian,尽管现在pkg有了新的维护者。)


6
sed -e'/./{H;$!d;}' -e'x;s/\n//g'

sed会将任何行添加到H包含至少一个字符的旧空间。它立即使d所有元素除掉最后一个之外的所有元素。唯一可以保留的行是空格,当sede x更改保留和模式空间并删除所有累积的\newline字符时,它们就位于这些行上。

如果要将仅包含<tabs><spaces>的行视为空白,请将/./上面的地址替换为/[^[:blank:]]/。要也压缩空间,请执行以下操作:

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

5

在看到了Gilles的perl和awk紧凑示例之后,我不愿发布此示例,但是我已经完成了练习,并且它是一个有效的脚本,已被合理地记录在案。这一点可能对某些人来说很有趣..(附评论!:)

该脚本认为空白行即使包含空格也为空白。
文本中的多个空格被压缩为一个空格。
尾随空格从文本行中删除。连续的空行折叠为单行。该脚本使顶部和底部空白行保持不变。

除了最简单的脚本外,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在注释中,意味着:将模式空间发送到sed的内部stdout处理。这并不意味着一定要打印到stdout。输出取决于sed的-n选项。例如。该q命令表示刷新并退出 ...比较这两个代码段:echo x |sed -e q打印x,不echo x |sed -ne q打印任何内容,而使用该p命令则将两次打印'x'一次,具体取决于-n选项。


+1表示好评。我看过太多没有评论的程序。
大卫·卡里

4

sed是将所有行连接到sed“保持空间” 的另一种解决方案,以便我们得到一个长字符串,最后将其复制到“模式空间”以进行模式匹配。

由于换行符将保留在sed“模式空间” 中的最后一个长字符串中,因此[^\n]\n\n[^\n]可以将空行替换为双换行符,并将其修改为[^\n]\n[^\n]

有关更多信息,请参见例如sed和多行搜索和替换

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 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.