我有一个从文本文件中读取的字符串,但是在Ubuntu Linux中,我尝试从最后删除它的换行符。
我用过各种方式。但是对于s/\n|\r/-/
(我看它是否发现任何替换任何新的行字符串)它会替换字符串,但是当我打印它时它仍然会转到下一行。此外,当我使用chomp
或chop
时,字符串将被完全删除。我找不到任何其他解决方案。我该如何解决这个问题?
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);
答案 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))