如何使用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_replace
space:
$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);
正则表达式详细信息:
\b
:匹配词边界
(?:
: 启动非捕获组
(?=(?:\pL\h+){3}\pL\b)
:前瞻断言我们有 3 个以上的单个字母,由 1 个以上的空格分隔
|
: 或
(?<!^)\G
:\G
断言位置在上一场比赛结束时。 (?<!^)
确保我们不匹配第一个匹配项的字符串开头
)
:结束非捕获组
(\pL)
:匹配单个字母并捕获
\h+
:后跟1+水平空格
(?=\pL\b)
: 断言前面只有一个字母
- 在替换中我们使用
这是我们捕获的空格左边的字母
你可以一次性完成:
(?i:(?<!\S)([a-z]) +((?1))|\G(?!\A) +((?1))\b)
解释:
(?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);
我们正在从 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_replace
space:
$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 次或更多次\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);
正则表达式详细信息:
\b
:匹配词边界(?:
: 启动非捕获组(?=(?:\pL\h+){3}\pL\b)
:前瞻断言我们有 3 个以上的单个字母,由 1 个以上的空格分隔|
: 或(?<!^)\G
:\G
断言位置在上一场比赛结束时。(?<!^)
确保我们不匹配第一个匹配项的字符串开头
)
:结束非捕获组(\pL)
:匹配单个字母并捕获\h+
:后跟1+水平空格(?=\pL\b)
: 断言前面只有一个字母- 在替换中我们使用
这是我们捕获的空格左边的字母
你可以一次性完成:
(?i:(?<!\S)([a-z]) +((?1))|\G(?!\A) +((?1))\b)
解释:
(?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);