删除单个字母之间的空格

时间:2010-11-19 19:42:33

标签: regex perl

我有一个字符串,可能包含由空格分隔的任意数量的单个字母。我正在寻找一个正则表达式(在Perl中),它将删除所有(未知数量)单个字母之间的空格。

例如:

ab c d应该成为ab cd

a bcd e f gh应该成为a bcd ef gh

a b c应该成为abc

abc d应保持不变(因为没有单个字母后跟或前面有单个空格)。

感谢任何想法。

8 个答案:

答案 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

我认为/希望这是你正在寻找的。

答案 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>