从Perl中的字符串中删除换行符

时间:2012-03-17 15:22:18

标签: regex string perl

我有一个从文本文件中读取的字符串,但是在Ubuntu Linux中,我尝试从最后删除它的换行符。

我用过各种方式。但是对于s/\n|\r/-/(我看它是否发现任何替换任何新的行字符串)它会替换字符串,但是当我打印它时它仍然会转到下一行。此外,当我使用chompchop时,字符串将被完全删除。我找不到任何其他解决方案。我该如何解决这个问题?

use strict;
use warnings;
use v5.12;
use utf8;
use encoding "utf-8";

open(MYINPUTFILE, "<:encoding(UTF-8)", "file.txt");

my @strings;
my @fileNames;
my @erroredFileNames;

my $delimiter;
my $extensions;
my $id;
my $surname;
my $name;

while (<MYINPUTFILE>)
{
    my ($line) = $_;
    my ($line2) = $_;
    if ($line !~ /^(((\X|[^\W_ ])+)(.docx)(\n|\r))/g) {
        #chop($line2);
        $line2 =~ s/^\n+//;
        print $line2 . " WRONG FORMAT!\n";
    }
    else {
        #print "INSERTED:".$13."\n";
        my($id) = $13;
        my($name) = $2;
        print $name . "\t" . $id . "\n";
        unshift(@fileNames, $line2);
        unshift(@strings, $line2 =~ /[^\W_]+/g);
    }
}
close(MYINPUTFILE);

5 个答案:

答案 0 :(得分:16)

删除Unicode换行字形(包括CRLF对)的正确方法是使用v5.10中引入的\R正则表达式字符。

强烈弃用use encoding pragma。您应该使用use open pragma,或在3-arg open的mode参数中使用编码,或使用binmode

 use v5.10;                     # minimal Perl version for \R support
 use utf8;                      # source is in UTF-8
 use warnings qw(FATAL utf8);   # encoding errors raise exceptions
 use open qw(:utf8 :std);       # default open mode, `backticks`, and std{in,out,err} are in UTF-8

 while (<>) {
     s/\R\z//;
     ...
 }

答案 1 :(得分:10)

您可能遇到因Windows文件而导致问题的行。例如,诸如“foo bar \ n”之类的字符串实际上将是“foo bar \ r \ n”。在Ubuntu上使用chomp时,您将删除the variable $/中包含的任何内容,即“\ n”。所以,剩下的就是“foo bar \ r \ n”。

这是一个微妙但非常常见的错误。例如,如果您打印“foo bar \ r \ n”并添加换行符,则不会发现错误:

my $var = "foo bar\r\n";
chomp $var;
print "$var\n";  # Remove and put back newline

但是当你将字符串与另一个字符串连接起来时,你会覆盖第一个字符串,因为\r会将输出句柄移动到字符串的开头。例如:

print "$var: WRONG\n";

它实际上是“foo bar \ r \ n:错误\ n”,但是\r之后的文本会导致以下文本回到第一部分的顶部:

foo bar\r           # \r resets position
 : WRONG\n          # Second line prints and overwrites

当第一行比第二行长时,这更明显。例如,尝试以下操作:

perl -we 'print "foo bar\rbaz\n"'

你会得到输出:

baz bar

解决方案是删除坏行结尾。您可以使用dos2unix命令执行此操作,也可以直接使用Perl执行此操作:

$line =~ s/[\r\n]+$//;

另外,请注意您的其他代码有些可怕。例如,您认为$13包含哪些内容?这是你前一个正则表达式中第13个括号所捕获的字符串。我很确定这个值总是未定义的,因为你没有13个括号。

您声明了两组$id$name。一个在循环外面,一个在顶部。这是非常糟糕的做法,IMO。只在他们需要的范围内声明变量,并且永远不要将所有声明都放在脚本的顶部,除非你明确希望它们是文件的全局。

为什么在$line$line2具有相同值时使用它们?只需使用$line

严肃地说,这是怎么回事:

if ($line !~ /^(((\X|[^\W_ ])+)(.docx)(\n|\r))/g) {

这看起来像是试图混淆,没有冒犯。三个嵌套的否定和一堆不必要的括号?

首先,因为它是一个if-else,只需交换它并反转正则表达式。其次,[^\W_]双重否定是相当混乱的。为什么不使用[A-Za-z0-9]?您可以将其拆分以便于解析:

if ($line =~ /^(.+)(\.docx)\s*$/) {
    my $pre = $1;
    my $ext = $2;

答案 2 :(得分:6)

您可以使用以下内容擦除换行符:

$line =~ s/[\n\r]//g;

但是,当您这样做时,您需要更改if语句中的正则表达式而不是查找它们。我也认为您不想在/g中使用if。你真的不应该有$line2

我也不会做这类事情:

print $line2." WRONG FORMAT!\n";

你可以做到

print "$line2 WRONG FORMAT!\n";

......相反。此外,print接受一个列表,因此您可以只使用逗号而不是连接字符串。

答案 3 :(得分:4)

您可以执行以下操作:

=~ tr/\n//

但真的chomp应该有效:

while (<filehandle>){
   chomp;
   ...
}

同样s/\n|\r//仅替换第一次出现的\r\n。如果要替换所有匹配项,则需要在末尾s/\r|\n//g使用全局修饰符。

注意:如果您为Windows添加\r,它通常会将其行结束为\r\n,因此您需要替换它们(例如s/(?:\r\n|\n)//),当然还有以上语句( s/\r|\n//g)使用全局修饰符无论如何都会照顾它。

答案 4 :(得分:3)

$variable = join('',split(/\n/,$variable))