【问题标题】:Remove all spaces from a string that are not enclosed in singlequotes or doublequotes删除字符串中未包含在单引号或双引号中的所有空格
【发布时间】:2020-10-23 16:41:10
【问题描述】:

我有这样的短字符串

$str = 'abc | xx ??   "1 x \' 3" d e f \' y " 5 \' x yz';

我想从一个字符串中删除所有没有用单引号或双引号括起来的空格。 不应更改任何用单引号或双引号括起来的字符。 因此,我期望:

$expected =  'abc|xx??"1 x \' 3"def\' y " 5 \'xyz';

我目前基于字符比较的解决方案如下:

function removeSpaces($string){
  $ret = $stop = "";
  for($i=0; $i < strlen($string);$i++){
    $char = $string[$i];
    if($stop == "") {
      if($char == " ") continue;
      if($char == "'" OR $char == '"') $stop = $char;
    }
    else {
      if($char == $stop) $stop = "";
    }
    $ret .= $char;
  }
  return $ret;
}

有更智能的解决方案吗?

【问题讨论】:

  • d e f \' y" 包围,这里的规则是什么?您找到第一个引号,然后,如果/当您找到下一个引号时,您开始删除空格?
  • 处理与PHP解释器相同。用双引号括起来的单引号和用单引号括起来的双引号被视为与任何其他字符一样。
  • @Thefourthbird 请不要将答案发布为 cmets。

标签: php regex string


【解决方案1】:

你可以使用

preg_replace('~(?<!\\\\)(?:\\\\{2})*(?:"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\')(*SKIP)(?!)|\s+~s', '', $str)

查看PHP demoregex demo

详情

  • (?&lt;!\\)(?:\\{2})* - 检查左边是否没有转义 \:任何数量的双反斜杠前面没有 \
  • (?:"[^"\\]*(?:\\.[^"\\]*)*"|'[^'\\]*(?:\\.[^'\\]*)*') - 允许转义序列的双引号或单引号字符串文字
  • (*SKIP)(?!) - 跳过匹配并从正则表达式失败的位置开始新的搜索
  • | - 或
  • \s+ - 1 个或多个空格。

请注意,单引号 PHP 字符串文字中的反斜杠用于形成字符串转义序列,因此在双反斜杠的帮助下“编码”文字反斜杠,并匹配文本中的文字反斜杠,两个这样的需要反斜杠,因此使用"\\\\"

【讨论】:

  • 非常适合我。很惊讶这可以通过正则表达式实现。非常感谢。
【解决方案2】:

您可以在一个组中捕获 "' 并使用任何转义变体或每个变体,直到遇到关闭匹配 '" 使用反向引用 \1

(?<!\\)(['"])(?:(?!(?:\1|\\)).|\\.)*+\1(*SKIP)(*FAIL)|\h+

Regex demo | Php demo

说明

  • (?&lt;!\\) 否定后视,断言不是 \ 直接向左
  • (['"]) 捕获 group 1,匹配 '"
  • (?:非捕获组
    • (?!(?:\1|\\)). 如果不直接在右侧的是第 1 组中的值或反斜杠,则匹配除换行符以外的任何字符
    • |或者
    • \\. 匹配转义字符
  • )*+关闭非捕获组并重复1次以上
  • \1 反向引用组 1 中捕获的内容(匹配 '"
  • (*SKIP)(*FAIL) 跳过比赛直到现在。 Read more about (*SKIP)(*FAIL)
  • |或者
  • \h+ 匹配 1+ 个要删除的水平空白字符

正如@Wiktor Stribiżew 在他的评论中指出的那样

在极少数情况下,这可能会在错误的位置匹配,即, 如果在 a 之前有一个文字反斜杠(不是转义的反斜杠) 应该跳过的单/双引号字符串。您需要添加 (?:\{2})* 在 (? 之后

模式将是:

(?<!\\)(?:\\{2})*(['"])(?:(?!(?:\1|\\)).|\\.)*+\1(*SKIP)(*FAIL)|\h+

Regex demo

【讨论】:

  • 也非常适合我。我接受这个解决方案,因为正则表达式有点短。谢谢。
  • @jspit 在极少数情况下,这可能会在错误的位置匹配,即,如果在应该跳过的单/双引号字符串之前有文字反斜杠(而不是转义反斜杠)。你需要在(?&lt;!\\)之后添加(?:\\{2})*
  • @WiktorStribiżew 这是一个很好的观点,在这种情况下,您的模式应该是公认的答案。 +1
  • 两个正则表达式对我来说都失败了这个字符串:“a\\'a b'” 我的字符串解决方案可以正常工作。但我可以为我的申请排除这种罕见的情况。因此,我可以毫无问题地使用您的解决方案。
【解决方案3】:

这是一个 3 步方法:

  1. 用占位符替换引号部分中的空格
  2. 删除所有空格
  3. 恢复引用部分中的空格
    $str = 'abc | xx ??   "1 x \' 3" d e f \' y " 5 \' x yz';
    echo 'input:  ' . $str . "\n";
    $result = preg_replace_callback( // replace spaces in quote sections with placeholder
        '|(["\'])(.*?)(\1)|',
        function ($matches) {
            $s = preg_replace('/ /', "\x01", $matches[2]);
            return $matches[1] . $s . $matches[3];
        },
        $str
    );
    $result = preg_replace('/ /', '', $result);     // remove all spaces
    $result = preg_replace('/\x01/', ' ', $result); // restore spaces in quote sections
    echo 'result: ' . $result;
    echo "\nexpect: " . 'abc|xx??"1 x \' 3"def\' y " 5 \'xyz';

输出:

input:  abc | xx ??   "1 x ' 3" d e f ' y " 5 ' x yz
result: abc|xx??"1 x ' 3"def' y " 5 'xyz
expect: abc|xx??"1 x ' 3"def' y " 5 'xyz

解释:

  1. 用占位符替换引号部分中的空格
  • 使用preg_replace_callback()
  • '|(["\'])(.*?)(\1)|' 匹配以 "' 开头和结尾的引用部分
  • (\1) 确保匹配结束引号("'
  • 在回调中,使用preg_replace() 将所有空格替换为不可打印的替换"\x01"
  1. 删除所有空格
  • 使用preg_replace() 删除所有空格
  • 替换与替换 "\x01" 不匹配,因此在引用部分缺少空格
  1. 恢复引用部分中的空格
  • 使用preg_replace()从替换"\x01"中恢复所有空格

【讨论】:

  • 我不会使用preg_,它需要在其中嵌套另一个preg_ 调用......并且最后还需要另外两个preg_ 调用来清理。您认为您的新答案在哪些方面为该页面增加了价值?
  • @mickmackusa:因为一个问题通常有不止一个答案/我认为提供选项很好。让读者自己决定什么有用,什么没用。
  • 但是如果你已经知道你的答案效率较低(因为它会打多个preg_ 电话,而之前的答案只打了 1 个preg_ 电话),那么你的答案只会浪费研究人员的时间当他们阅读没有任何好处的答案时。因此,我再次向您提出的问题是:与其他答案相比,这个效率较低、不那么直接、更冗长的答案有什么价值?它是否以某种方式提供了更高的准确性?我不知道,因为我没有测试任何答案,但我几乎可以把钱放在 Wiktor 的可靠答案上。
  • @mickmackusa:我同意,对于这个特定的用例,单行正则表达式更容易,而且可能更快。这个答案的价值:学习和教育。在某些用例中,多遍正则表达式可以有效解决复杂问题,例如解析嵌套结构:twiki.org/cgi-bin/view/Blog/BlogEntry201109x3
猜你喜欢
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多