【问题标题】:Oracle SQL regexp_substr number extraction behaviorOracle SQL regexp_substr 数提取行为
【发布时间】:2020-04-23 12:30:29
【问题描述】:

从某种意义上说,我已经回答了自己的问题,但我正在努力更好地理解答案:

当使用 regexp_substr(在 oracle 中)提取第一次出现的数字(单个或多个数字)时,修饰符 *+ 如何/为什么会影响结果?为什么+ 提供我正在寻找的行为而* 没有? * 是我在大多数正则表达式中的默认用法,所以我很惊讶它不适合我的需要。

例如,在下面:


select test, 
regexp_substr(TEST,'\d') Pattern1,
regexp_substr(TEST,'\d*') Pattern2, 
regexp_substr(TEST,'\d+') Pattern3
from (
select '123 W' TEST from dual
union
select 'W 123' TEST from dual
);

regexp_substr(TEST,'\d*') 的使用为输入“W 123”返回空值 - 因为字符串中存在“零个或多个”数字,我对这种行为感到困惑。我也很困惑为什么它确实适用于字符串 '123 W'

我的理解是,* 表示它后面的元素出现零次或多次,+ 表示前面元素出现 1 次或多次。在为 pattern2 [\d*] 提供的示例中,为什么它成功地从“123 W”捕获“123”,但它没有从“W 123”中获取 123,因为确实存在零个或多个数字,它们只是不存在在字符串的开头。使用* 是否附加了额外的[隐含]逻辑?

注意:我环顾了一会,试图找到帮助我从“W 123”中捕获“123”的类似问题,但我发现最接近的是 regexp_replace 的变体,它不能满足我的需求。

【问题讨论】:

  • 这是一个非常好的问题,因为我同意你的观点,似乎\d* 将匹配字符串W 123 中的123。我在使用* 时遇到了类似的问题。这可能会为您提供一些见解:mariusschulz.com/blog/…

标签: sql regex oracle


【解决方案1】:

所以 regexp_count 表示有四个匹配 \d* 模式的子字符串。 其中第三个是“123”。这意味着第一个和第二个是从 W 和空格派生的,而你所拥有的是一个零长度的结果,它“消耗”了源字符串的一个字符。

select test, 
    regexp_count(TEST,'\d*') Pattern2_c, 
    regexp_substr(TEST,'\d*') Pattern2,
    regexp_substr(TEST,'\d*',1,1) Pattern2_1,
    regexp_substr(TEST,'\d*',1,2) Pattern2_2,
    regexp_substr(TEST,'\d*',1,3) Pattern2_3,
    regexp_substr(TEST,'\d*',1,4) Pattern2_4
 from (select '123 W' TEST from dual
    union
    select 'W 123' TEST from dual
    );

Oracle 对零长度字符串和 null 有一个奇怪的地方。

结果“感觉”不正确,但是如果你问计算机深刻的哲学问题,即字符串中包含多少个零长度子字符串,我不会打赌任何答案。

【讨论】:

  • Oracle 假设零长度字符串是 NULL 让我抓狂。我遇到了很多与此相关的问题。
  • “多少个零长度子串”?答案是length(str) + 1 在所有情况下和所有正则表达式风格中,您是否见过任何其他结果?
【解决方案2】:

想了想,其实是有道理的。模式\d* 表示匹配任意数字 次或多次。这里的问题是字符串的开头总是会匹配这个模式,因为有零次或多次。

如果字符串以数字开头,那么它将包含这些数字,因此给定123 W,模式匹配123。但是,给定模式W 123,该模式也在开头匹配,但它匹配 0 个字符。这就是您得到NULL 结果的原因。

这是一个通用的正则表达式,而不是 Oracle 的东西。你必须小心* 量词。

这里有两个正则表达式小提琴示例来说明这一点,使用字符串W 123

【讨论】:

  • 这是正确的 - 附加说明(也适用于所有正则表达式,不仅仅是 Oracle)从字符串 first 的开头尝试匹配。如果未找到匹配项,则从第一个字符开始尝试匹配;如果在那里也没有找到匹配项,则...(重复)。在\d* 的情况下,总是从字符串的开头开始找到匹配项(有时长度为零,有时更长),因此不需要或尝试从其他位置开始进行其他匹配。就这么简单!
  • @mathguy 是的,感谢您的澄清。这就是我想说的,但你说得好多了!
  • 重要的想法——在我清楚地理解这一点之前,我一直在努力使用正则表达式——字符串有“字符”但也有“位置”(在字符之间,在第一个字符之前和最后一个字符之后)。正则表达式从这样的“位置”延伸到另一个“位置”(或者对于长度为零的匹配,延伸到相同的位置)。像 ^ 和 $ 这样的锚点,但也 - Oracle 不支持! - 单词边界、前瞻和环视等都是关于字符串中的“位置”,而不是“字符”。
猜你喜欢
  • 2020-10-10
  • 2021-04-23
  • 1970-01-01
  • 2012-06-26
  • 2019-03-21
  • 2021-12-18
  • 2016-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多