如何使用preg_replace去除过多的单个空格

How to use preg_replace to remove excessive single spaces

我们正在从 PDF 文件中提取文本,结果中包含格式错误文本的频率很高。特别是在单词的字符之间添加空格。例如SEATTLE 返回为 S E A T T L E

是否有 preg_replace 的 RegEx 表达式可以在 n 个单个字符 "words" 的情况下删除任何空格?具体来说,从任何超过 3 个单个字母字符且由空格分隔的字符串中删除空格?

如果用谷歌搜索一段时间,但甚至无法想象如何构建表达式。如评论中所述,我不希望删除所有空格,但仅当出现 >3 个单个字母字符时才删除,例如Welcome to the Greater S E A T T L E area 应该变成 Welcome to the Greater SEATTLE area。结果将用于全文搜索,因此不区分大小写。

您可以使用带有 preg_replace_callback 的简单方法。匹配匿名函数中的'~\b[A-Za-z](?: [A-Za-z]){2,}\b~'str_replacespace:

$regex = '~\b[A-Za-z](?: [A-Za-z]){2,}\b~';
$result = preg_replace_callback($regex, function($m) {
     return str_replace(" ", "", $m[0]);
}, $s);

参见regex demo

要仅匹配大写字母序列,请从模式中删除 a-z

$regex = '~\b[A-Z](?: [A-Z]){2,}\b~';

还有一件事:可能有 soft/hard space 标签,其他类型的白色 space。然后,使用

$regex = '~\b[A-Za-z](?:\h[A-Za-z]){2,}\b~u';
                        ^^                ^

最后,要匹配任何 Unicode 字母,请使用 \p{L}(只匹配大写字母,\p{Lu})而不是 [a-zA-Z]:

$regex = '~\b\p{L}(?:\h\p{L}){2,}\b~u';

注意:在某些情况下它很可能无法工作,例如当有一个字母的单词时。您将不得不处理这些情况 separately/manually。无论如何,没有安全的仅使用正则表达式的方法来解决 OCR 问题。

图案详情

  • \b - 单词边界
  • [A-Za-z] - 一个字母
  • (?: [A-Za-z]){2,} - 出现 2 次或更多次
    • - a space(\h 匹配任何类型的水平白色space)
    • [A-Za-z] - 一个字母
  • \b - 单词边界

当使用 u 修饰符时,\h 变为 Unicode 识别。

您可以将这种纯正则表达式方法与环视和 \G:

结合使用
$re = '~\b(?:(?=(?:\pL\h+){3}\pL\b)|(?<!^)\G)(\pL)\h+(?=\pL\b)~';

$repl = preg_replace($re, '', $str);

RegEx Demo

正则表达式详细信息:

  • \b:匹配词边界
  • (?:: 启动非捕获组
    • (?=(?:\pL\h+){3}\pL\b):前瞻断言我们有 3 个以上的单个字母,由 1 个以上的空格分隔
    • |: 或
    • (?<!^)\G\G 断言位置在上一场比赛结束时。 (?<!^) 确保我们不匹配第一个匹配项的字符串开头
  • ):结束非捕获组
  • (\pL):匹配单个字母并捕获
  • \h+:后跟1+水平空格
  • (?=\pL\b): 断言前面只有一个字母
  • 替换中我们使用这是我们捕获的空格左边的字母

你可以一次性完成:

(?i:(?<!\S)([a-z]) +((?1))|\G(?!\A) +((?1))\b)

live demo here

解释:

(?i: # Start of non-capturing group with case-insensitive modifier on
    (?<!\S) # Negative lookbehind to ensure there is no leading non-whitespace character
    ([a-z]) + # Capture one letter and at least one space
    ((?1)) # Capture one letter in 2nd capturing group
    | # Or
    \G(?!\A) + # Start match from where previous match ends 
               # with matching spaces
    ((?1))\b # Match a letter at word boundary
) # End of non-capturing group

PHP代码:

$str = preg_replace('~(?i:(?<!\S)([a-z]) +((?1))|\G(?!\A) +((?1))\b)~', '', $str);