如何除了最后一个点以外从字符串中删除所有点?

6
我想从一个字符串中删除除最后一个以外的所有.
可以使用JavaScript来实现。
var s='1.2.3.4';
s=s.split('.');
s.splice(s.length-1,0,'.');
s.join('');

但是当在Perl中尝试相同的操作时

my @parts = split /./, $s;
my @a = splice @parts, $#parts-1,0;
$s = join "", @a;

我理解

Modification of non-creatable array value attempted, subscript -2 at ./test.pl line 15.

问题

有谁能在Perl中解决这个问题吗?

7个回答

9
我会在任务中使用带有正向预查的正则表达式(regexp)来完成,这要用perl语言实现。
perl -pe 's/\.(?=.*\.)//g' <<<"1.2.3.4"

结果:

123.4

编辑:使用split修复您的解决方案:

use warnings;
use strict;

my $s = '1.2.3.4';
my @parts = split /\./, $s; 
$s = join( "", @parts[0 .. $#parts-1] ) . '.' . $parts[$#parts];
printf "$s\n";

2
在那里过度使用 printf,当 print 就足够了。 - TLP
1
你理解正向先行断言的方式是这样的吗?:首先执行括号中的内容,由于.*是贪婪的,它会找到最后一个.,并且在其路径上遇到的所有.都被替换为空。 - Sandra Schlichting
2
@SandraSchlichting 不,正则表达式将找到一个字面上的句点,并且前瞻将断言在更远的地方至少有一个字面上的句点。当断言失败时,您将找到最后一个句点。 - TLP
1
@TLP:感谢TLP的解释。+1 - Birei
1
分割解决方案无法处理 12341.2.3.4.,如果这很重要的话。 - ikegami
@ikegami:是的,你说得对。这些情况需要特殊处理。我不会更改答案,因为它对问题的作者来说似乎很好。 - Birei

4

首先,在 split 指令中需要转义点号:my @parts = split /\./, $s;


4
您的split使用了正则表达式/./,其中.被视为通配符。如果您想要在一个实际点上分割,请使用转义字符进行转义:
... split /\./, $s;

splice接受参数ARRAY或EXPR、OFFSET、LENGTH、LIST(Perl v5.14)。如果LENGTH为0,则不会删除任何内容,因此也不会返回任何内容。

您的代码与您所说的尝试做什么的相矛盾,因此我不太确定您真正想要做什么,但是假设您想要除了最后一个以外删除所有句号,我希望您能做出类似于以下的操作:

my @parts = split /\./, $s;
my $end   = pop @parts;
$s        = join "", @parts, ".$end";

也许可以操作分割。
my @parts = split /\./, $s;
my $limit = @parts - 1;  # the field count for split
$s        = join "", split /\./, $s, $limit;

基本上,找出你的字符串会被分成多少个字段,减去一个后,执行新的分割并将LIMIT设置为那个值。

第一个不能处理 1234,第二个也不能处理 1.2.3.4.,如果这很重要的话。 - ikegami

3

当你感到疑惑时,使用诊断工具;

$ perl -Mdiagnostics -le " splice @ARGV, -1 ,0 "
Modification of non-creatable array value attempted, subscript -1 at -e line 1 (#1)
    (F) You tried to make an array value spring into existence, and the
    subscript was probably negative, even counting from end of the array
    backwards.

Uncaught exception from user code:
        Modification of non-creatable array value attempted, subscript -1 at -e line 1.
 at -e line 1.

$ perl -Mdiagnostics -le " splice @ARGV, -1 ,0 " argv now not empty

我觉得你不应该使用负偏移量,而是应该使用偏移量0和数组长度减去1(也称为最后一个索引)的大小。

$ perl -le " print for splice @ARGV, 0, $#ARGV-1 " a b c
a

哎呀,$#ARGV 是最后一个索引,而不是 $#ARGV-1,所以

$ perl -le " print for splice @ARGV, 0, $#ARGV " a b c
a
b

但是如果你仍然需要一些算术运算,可以使用@ARGV,因为在标量上下文中它是数组的大小。

$ perl -le " print for splice @ARGV, 0, @ARGV-1 " a b c
a
b

使用非负偏移量进行splice的附加好处是什么?当数组为空时,它不会出现故障。
$ perl -le " print for splice @ARGV, 0, 10 "

1

这看起来更像是你在Perl中尝试做的事情

my @parts = split /\./, $s;
$s = join('', splice(@parts, 0, -1)) . '.' . $parts[-1];

如果有影响的话,它不能处理12341.2.3.4. - ikegami

0
你在 splice 调用中漏掉了 '.' 。这是正确的写法:
use strict;
use warnings;

my $s = '1.2.3.4';

my @parts = split /\./, $s;
splice @parts, -1, 0, '.';
$s = join "", @parts;

如果有影响的话,它不处理12341.2.3.4. - ikegami

0

split 的第一个参数是一个正则表达式。在正则表达式中,"." 表示 "匹配任何字符"(使用 /s)或 "匹配除 LF 以外的任何字符"(不使用 /s)。您需要转义它才能匹配字面上的 "."。

my @parts = split(/\./, $s, -1);            # "-1" to handle "1.2.3.4."
splice(@parts, -1, 0, '.') if @parts > 2;   # "if" to handle "1234"
$s = join('', @parts);

一个替换也可以实现:

$s =~ s/\.(?=.*\.)//sg;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接