我有一个字符串,可能包含由空格分隔的任意数量的单个字母。我正在寻找一个正则表达式(在Perl中),它将删除所有(未知数量)单个字母之间的空格。
例如:
ab c d
应该成为ab cd
a bcd e f gh
应该成为a bcd ef gh
a b c
应该成为abc
和
abc d
应保持不变(因为没有单个字母后跟或前面有单个空格)。
感谢任何想法。
答案 0 :(得分:7)
您的说明与您的示例不符。在我看来,你想要删除任何空格,即(1)前面有一个字母,前面没有字母,(2)后跟一个字母,后面跟着一个字母。这些条件可以精确地表示为嵌套的外观:
/(?<=(?<!\pL)\pL) (?=\pL(?!\pL))/
测试:
use strict;
use warnings;
use Test::Simple tests => 4;
sub clean {
(my $x = shift) =~ s/(?<=(?<!\pL)\pL) (?=\pL(?!\pL))//g;
$x;
}
ok(clean('ab c d') eq 'ab cd');
ok(clean('a bcd e f gh') eq 'a bcd ef gh');
ok(clean('a b c') eq 'abc');
ok(clean('ab c d') eq 'ab cd');
输出:
1..4
ok 1
ok 2
ok 3
ok 4
我假设你真的是指一个空格字符(U + 0020);如果要匹配任何空格,可能需要用\s+
替换空格。
答案 1 :(得分:5)
您可以使用前瞻和后瞻断言来执行此操作,如perldoc perlre中所述:
use strict;
use warnings;
use Test::More;
is(tran('ab c d'), 'ab cd');
is(tran('a bcd e f gh'), 'a bcd ef gh');
is(tran('a b c'), 'abc');
is(tran('abc d'), 'abc d');
sub tran
{
my $input = shift;
(my $output = $input) =~ s/(?<![[:lower:]])([[:lower:]]) (?=[[:lower:]])/$1/g;
return $output;
}
done_testing;
注意当前代码在第二个测试用例上失败,因为输出为:
ok 1
not ok 2
# Failed test at test.pl line 7.
# got: 'abcd efgh'
# expected: 'a bcd ef gh'
ok 3
ok 4
1..4
# Looks like you failed 1 test of 4.
我这样离开了,因为你的第二个和第三个例子似乎相互矛盾,关于如何处理前导单个字符。但是,这个框架应该足以让您尝试不同的前瞻和外观,以获得您正在寻找的确切结果。
答案 2 :(得分:1)
这段代码
#!/usr/bin/perl
use strict;
my @strings = ('a b c', 'ab c d', 'a bcd e f gh', 'abc d');
foreach my $string (@strings) {
print "$string --> ";
$string =~ s/\b(\w)\s+(?=\w\b)/$1/g; # the only line that actually matters
print "$string\n";
}
打印出来:
a b c --> abc
ab c d --> ab cd
a bcd e f gh --> a bcd ef gh
abc d --> abc d
我认为/希望这是你正在寻找的。 p>
答案 3 :(得分:0)
这应该可以解决问题:
my $str = ...;
$str =~ s/ \b(\w) \s+ (\w)\b /$1$2/gx;
删除所有单个非空格字符之间的空格。如果需要,可以使用限制性更强的字符类替换\S
。您可能还需要处理与标点符号相关的一些边缘情况,但我无法从您提供的信息中猜出。
正如以太有用地指出的那样,一个案例就失败了。这是一个应该工作的版本(虽然不像第一个那么干净):
s/ \b(\w) ( (?:\s+ \w\b)+ ) /$1 . join '', split m|\s+|, $2/gex;
我喜欢Ether的基于测试的方法(模仿是最真诚的奉承形式):
use warnings;
use strict;
use Test::Magic tests => 4;
sub clean {
(my $x = shift) =~ s{\b(\w) ((?: \s+ (\w)\b)+)}
{$1 . join '', split m|\s+|, $2}gex;
$x
}
test 'space removal',
is clean('ab c d') eq 'ab cd',
is clean('a bcd e f gh') eq 'a bcd ef gh',
is clean('a b c') eq 'abc',
is clean('abc d') eq 'abc d';
返回:
1..4
ok 1 - space removal 1
ok 2 - space removal 2
ok 3 - space removal 3
ok 4 - space removal 4
答案 4 :(得分:0)
这不是一个正则表达式,但由于我天性懒惰,我会这样做。
#!/usr/bin/env perl
use warnings;
use 5.012;
my @strings = ('a b c', 'ab c d', 'a bcd e f gh', 'abc d');
for my $string ( @strings ) {
my @s; my $t = '';
for my $el ( split /\s+/, $string ) {
if ( length $el > 1 ) {
push @s, $t if $t;
$t = '';
push @s, $el;
} else { $t .= $el; }
}
push @s, $t if $t;
say "@s";
}
好的,我的方式是最慢的:
no_regex 130619/s -- -60% -61% -63%
Alan_Moore 323328/s 148% -- -4% -8%
Eric_Storm 336748/s 158% 4% -- -5%
canavanin 352654/s 170% 9% 5% --
我没有包含以太的代码,因为(正如他已经测试过的)它会返回不同的结果。
答案 5 :(得分:0)
现在我有最慢和最快的。
#!/usr/bin/perl
use 5.012;
use warnings;
use Benchmark qw(cmpthese);
my @strings = ('a b c', 'ab c d', 'a bcd e f gh', 'abc d');
cmpthese( 0, {
Eric_Storm => sub{ for my $string (@strings) { $string =~ s{\b(\w) ((?: \s+ (\w)\b)+)}{$1 . join '', split m|\s+|, $2}gex; } },
canavanin => sub{ for my $string (@strings) { $string =~ s/\b(\w)\s+(?=\w\b)/$1/g; } },
Alan_Moore => sub{ for my $string (@strings) { $string =~ s/(?<=(?<!\pL)\pL) (?=\pL(?!\pL))//g; } },
keep_uni => sub{ for my $string (@strings) { $string =~ s/\PL\pL\K (?=\pL(?!\pL))//g; } },
keep_asc => sub{ for my $string (@strings) { $string =~ s/[^a-zA-Z][a-zA-Z]\K (?=[a-zA-Z](?![a-zA-Z]))//g; } },
no_regex => sub{ for my $string (@strings) { my @s; my $t = '';
for my $el (split /\s+/, $string) {if (length $el > 1) { push @s, $t if $t; $t = ''; push @s, $el; } else { $t .= $el; } }
push @s, $t if $t;
#say "@s";
} },
});
Rate no_regex Alan_Moore Eric_Storm canavanin keep_uni keep_asc
no_regex 98682/s -- -64% -65% -66% -81% -87%
Alan_Moore 274019/s 178% -- -3% -6% -48% -63%
Eric_Storm 282855/s 187% 3% -- -3% -46% -62%
canavanin 291585/s 195% 6% 3% -- -45% -60%
keep_uni 528014/s 435% 93% 87% 81% -- -28%
keep_asc 735254/s 645% 168% 160% 152% 39% --
答案 6 :(得分:0)
这将完成这项工作。
(?<=\b\w)\s(?=\w\b)
答案 7 :(得分:0)
嗨,我已经编写了简单的javascript来做到这一点,它很简单,您可以转换为任何语言。
function compressSingleSpace(source){
let words = source.split(" ");
let finalWords = [];
let tempWord = "";
for(let i=0;i<words.length;i++){
if(tempWord!='' && words[i].length>1){
finalWords.push(tempWord);
tempWord = '';
}
if(words[i].length>1){
finalWords.push(words[i]);
}else{
tempWord += words[i];
}
}
if(tempWord!=''){
finalWords.push(tempWord);
}
source = finalWords.join(" ");
return source;
}
function convertInput(){
let str = document.getElementById("inputWords").value;
document.getElementById("firstInput").innerHTML = str;
let compressed = compressSingleSpace(str);
document.getElementById("finalOutput").innerHTML = compressed;
}
label{
font-size:20px;
margin:10px;
}
input{
margin:10px;
font-size:15px;
padding:10px;
}
input[type="button"]{
cursor:pointer;
background: #ccc;
}
#firstInput{
color:red;
font-size:20px;
margin:10px;
}
#finalOutput{
color:green;
font-size:20px;
margin:10px;
}
<label for="inputWords">Enter your input and press Convert</label><br>
<input id="inputWords" value="check this site p e t z l o v e r . c o m thanks">
<input type="button" onclick="convertInput(this.value)" value="Convert" >
<div id="firstInput">check this site p e t z l o v e r . c o m thanks</div>
<div id="finalOutput">check this site petzlover.com thanks</div>