正则表达式:分离包含字母数字或仅数字的字符串

3

你能帮助我拆分一个字符串(或在可能的情况下,每次从字母数字到数字或数字到字母数字的变化时放置一个空格)吗?

因此,像D2c1 22这样的字符串应该看起来像D 2 c 1 22。最好的方法是在从字母数字到数字的每次变化处放置一个空格。


1
我可以帮助你,但不能替你完成。 - Shiplu Mokaddim
1
有点相关的是这个关于自然排序的问题,其中包含了数字和非数字序列的混合字符串。那里的一些查询可能对您有用。https://dev59.com/amcs5IYBdhLWcg3wSiJo - Craig Ringer
4个回答

3
您可以使用这个正则表达式来查找切换位置:
(?<=\d)(?=\D)|(?<=\D)(?=\d)

这样做:
"234kjh23ljkgh34klj2345klj".gsub(/(?<=\d)(?=\D)|(?<=\D)(?=\d)/, " ")
=> "234 kjh 23 ljkgh 34 klj 2345 klj"

编辑: 不使用零长度正向先行断言和零长度反向先行断言:

"234kjh23ljkgh34klj2345klj".gsub(/(\d)(\D)/, "#{$1} #{$2}").gsub(/(\D)(\d)/, "#{$2} #{$1}")
=> "23 jk 5 jkgk 5 lk 534 lj"

很不幸,Pg(至少9.2版本)似乎无法理解该正则表达式。regress=> select regexp_replace( '234kjh23ljkgh34klj2345klj', '(?<=\d)(?=\D)|(?<=\D)(?=\d)', ' ', 'g'); 出现了 ERROR: invalid regular expression: quantifier operand invalid 错误。请参见 http://sqlfiddle.com/#!12/d41d8/148。 - Craig Ringer
它可能无法处理零长度的前瞻和后顾。在这种情况下,您可以使用类似于以下方法(这是Ruby语法,因此您需要进行调整):“234kjh23ljkgh34klj2345klj”。gsub(/(\ d)(\ D)/,“#{$ 1}#{$ 2}”)。gsub(/(\ D)(\ d)/,“#{$ 2}#{$ 1}”) =>“23 jk 5 jkgk 5 lk 534 lj” - davidrac
2
pg 中前瞻语法(来自文档):(?=re) 正向前瞻匹配任何子字符串与 re 匹配的起始点(仅限 AREs) (?!re) 负向前瞻匹配任何没有子字符串与 re 匹配的起始点(仅限 AREs) - davidrac
非常感谢您的解释,但您能否将上述内容翻译成适合pg的格式?我对正则表达式真的不太了解。 - calimero

2
这里提供一种经过PostgreSQL测试并验证可行的方法。它可能有些复杂,因此性能可能会有所影响。
CREATE AGGREGATE array_cat_agg (
  BASETYPE = anyarray,
  SFUNC = array_cat,
  STYPE = anyarray
);
SELECT array_to_string(array_cat_agg(a), ' ')
FROM regexp_matches('234kjh23ljkgh34klj2345klj', '(\D*)(\d*)', 'g') x(a);

我们需要使用 array_cat_agg,因为常规的 array_agg 无法聚合数组中的数组。
另外,@davidrac 的一种方法也可以在 PostgreSQL 中使用,并且可能会表现得更好(尽管我没有测试过):
SELECT regexp_replace(
  regexp_replace(
     '234kjh23ljkgh34klj2345klj', '(\d)(\D)', '\1 \2', 'g'
  ), '(\D)(\d)', '\1 \2', 'g');

这是在两个步骤中执行替换。首先,在数字序列结束和非数字序列开始处插入一个空格。然后,在另一个步骤中,在非数字序列结束和数字序列开始处插入空格。
更新:这里有一个改进的表述:
SELECT trim(regexp_replace('234kjh23ljkgh34klj2345klj', '(?!\d)(\D+)|(?!\D)(\d+)', '\1\2 ', 'g'));

1
@davidrac 已确认你的第二个公式在 Pg 上有效。 我还重新为 Pg 改写了它,包括之前(丑陋的)实现方式。 我赞同你的答案。 - Craig Ringer
1
@calimero 你是否在使用旧版本的PostgreSQL(9.0或更早),在standard_conforming_strings更改之前?尝试 SET standard_conforming_strings = on; 然后再次测试。(这就是为什么你在问题中总是提到你的PostgreSQL版本)。如果使用 standard_conforming_strings 可以正常工作,则(a)升级PostgreSQL并(b)参见http://www.postgresql.org/docs/current/static/runtime-config-compatible.html#GUC-STANDARD-CONFORMING-STRINGS,了解如何使查询在不升级PostgreSQL的情况下正常工作。基本上,不要使用 '\',而是使用 E'\\';双反斜杠并使用 E'' - Craig Ringer
它是psql 8.1版本。但我不被允许更改该参数(错误:无法更改参数“standard_conforming_strings”)。我想我必须等管理员到来。 - calimero
1
@calimero 不,只需根据上面的文档链接重写查询以适应您(实话说有些过时)的PostgreSQL版本。'(\d)(\D)' 变成 E'(\\d)(\\D)' 等等。顺便说一下,您需要紧急开始计划升级您已经到了生命周期末期且已经过时的PostgreSQL,并且在问题中始终提及您的版本,尤其是因为它非常过时。另请参阅 http://www.postgresql.org/support/versioning/。 - Craig Ringer
非常感谢大家的帮助!!!是的,我们已经有一个运行中的PSQL 9.2实例,并为其准备了一个新模式。但是这个查询必须在旧系统中快速完成,所以再次感谢您的大力帮助! :) - calimero
显示剩余2条评论

1
最好的方法是在从字母数字转换为数字时每次插入一个空格。这并不难做到:
$ echo "D2c1 22" | sed 's|\([a-ZA-Z]\)\([0-9]\)|\1 \2|g;s|\([0-9]\)\([a-ZA-Z]\)|\1 \2|g'
D 2 c 1 22

这里我使用了sed和它的正则表达式,因为您没有提到使用哪种语言。主要思路是使用两个正则表达式,将字母替换为数字,数字替换为字母,并应用于第一个字符、空格和第二个字符。


非常感谢您的快速回复。我想在PostgreSQL中完成它。 - calimero

1

您可以使用正则表达式进行匹配

(?<=[a-z])(?=[0-9])|(?<=[0-9])(?=[a-z])

将其替换为空格。

在Perl中查看


根据@davidrac的解决方案,似乎Pg的正则表达式引擎无法处理这个问题。请参见http://sqlfiddle.com/#!12/d41d8/148 - Craig Ringer
通常最好使用\d\D,这样您就可以处理任何数字和非数字序列,而不仅仅是小写字母数字。 - Craig Ringer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接