PostgreSQL,regex将文本字段与数值匹配

mpbci0fu  于 2022-11-18  发布在  PostgreSQL
关注(0)|答案(2)|浏览(196)

我有一个文本类型的列,其中包含随机值。其中一些是数字,一些是文本,还有一些是文本和数字的混合。我试图将它们区分为:

myfield   is_numeric
____________________
-4        true
0004      true
4.00      true
dog       false
D04       false
04f       false

而其他的是名字和其他字符串。我用的是正则表达式

SELECT id,
       myfield 
       (myfield::varchar~ '^-?[0-9]*.?[0-9]*$') is_numeric 
FROM mytable

但是,我注意到像D0404c这样的值对于该正则表达式返回true,对于我的用例来说,这是一个误报。
为什么会出现这种情况?看起来^不一定匹配整个值,而是匹配该值的任何有效子字符串。然而,像D04f这样的值确实返回false,因此即使字段中存在数字子字符串,^$运算符的组合也能完成它的工作。
我暂时使用:

SELECT id, 
       myfield
       (myfield::varchar ~ '^-?[0-9]*.?[0-9]*$' 
       AND myfield::varchar !~ '[^0-9\-\.]') is_numeric 
FROM mytable

但这似乎是无效的(并且不排除双点),我仍然想知道为什么正则表达式正确地排除了以非数字字符开头和结尾的字符串,而对只包含尾随或前导非数字字符的字符串错误地返回true。

pbpqsu0x

pbpqsu0x1#

这对你有用吗?

^-?[0-9]+\.?[0-9]*$

我假定-0.07.5是无效的(出现双点)。
D04也将返回false。
原始正则表达式的问题是没有转义点,因此它将匹配任何字符,包括D04中的D
希望能有所帮助。

vltsax25

vltsax252#

任何基于正则表达式的解决方案最多只能重新发明PostgreSQL自己的逻辑,用于将text转换为numeric(或任何其他类型)。
为了与PostgreSQL自己的类型定义保持一致,最简单的方法是直接使用它:

create or replace function is_numeric(arg text) 
returns boolean language plpgsql as $$
begin
    perform cast(arg as numeric); --if `arg` is not a valid `numeric`, throws exception here 
    return true;
exception when others then 
    return false;
end $$;

更一般的形式:

create or replace function is_interpretable_as(arg text, arg_type text) 
returns boolean language plpgsql as $$
begin
    execute format('select cast(%L as %s)', arg, arg_type);
    return true;
exception when others then
    return false;
end $$;

请注意,无论参数和/或类型是否无效,都将返回false
Demo

相关问题