如何在正则表达式中获取所有组合(无替换的有序采样)
How to get all combinations (ordered sampling without replacement) in regex
我正在尝试将逗号分隔的数字字符串与 sql 查询中的特定模式相匹配。我过去成功地使用正则表达式解决了类似的问题,所以我也试图让它们在这里工作。问题如下:
- 该字符串可以包含范围内的任何数字(例如 1-4)恰好 0-1 次。
- 两个数字以逗号分隔
- 数字必须按升序排列
(我认为这是一种没有放回的有序抽样的情况)
以 1-4 为例,以下条目应匹配:
1
1,2
1,3
1,4
1,2,3
1,2,4
1,3,4
1,2,3,4
2
2,3
2,4
3
3,4
4
这些不应该:
q dawda 323123 a3 a1 1aa,1234 4321 a4,32,1a 1112222334411
1,,2,33,444, 11,12,a 234 2,2,3 33 3,3,3 3,34 34 123 1,4,4,4a 1,444
我目前最好的尝试是:
\b[1-4][\,]?[2-4]?[\,]?[3-4]?[\,]?[4]?\b
这仍然有两个主要缺点:
- 它提供了很多误报。数字出现一次后不消除。
- 当数字范围增加时,它会变得相当长,例如1-18也已经可以了,可以考虑更大的范围。
我使用 regexpal 进行测试。
旁注:
- 因为我正在使用 sql,所以可以用另一种语言实现一些算法来生成所有可能的组合并将它们保存在可用于连接的 table 中,请参阅例如How to get all possible combinations of a list’s elements?。我只想依靠它作为最后的手段,因为将涉及新 table 的创建,并且这些将包含很多条目。
- 使用正则表达式的结果 sql 语句在 Postgres 和 Oracle 上应该 运行。
- 正例集也称为"powerset"。
编辑:澄清了正例列表
我不会为此使用正则表达式,例如要求 "have to be unique" 和 "have to be in ascending order" 不能真正用正则表达式表达(至少我想不出一种方法来做到这一点)。
由于您还需要有一个在 Postgres 和 Oracle 中相同的表达式,我会创建一个函数来检查这样的列表,然后在该函数中隐藏 DBMS 特定的实现。
对于 Postgres,我会使用其数组处理功能来实现该功能:
create or replace function is_valid(p_input text)
returns boolean
as
$$
select coalesce(array_agg(x order by x) = string_to_array(p_input, ','), false)
from (
select distinct x
from unnest(string_to_array(p_input,',')) as t(x)
where x ~ '^[0-9]+$' -- only numbers
) t
where x::int between 1 and 4 -- the cast is safe as the inner query only returns valid numbers
$$
language sql;
内部查询return将输入列表中的所有(不同)元素作为单独的数字。外部查询然后将其聚合回所需范围和数字顺序中的值。如果该结果与输入不同,则输入无效。
然后使用以下示例数据:
with sample_data (input) as (
values
('1'),
('1,2'),
('1,3'),
('1,4'),
('1,2,3'),
('1,2,4'),
('foo'),
('1aa,1234'),
('1,,2,33,444,')
)
select input, is_valid(input)
from sample_data;
会 return:
input | is_valid
-------------+---------
1 | true
1,2 | true
1,3 | true
1,4 | true
1,2,3 | true
1,2,4 | true
foo | false
1aa,1234 | false
1,,2,33,444, | false
如果您想在 Postgres 和 Oracle 中使用相同的函数,您可能需要在 Postgres 中使用 returns integer
,因为 Oracle 仍然不支持 SQL[=14= 中的布尔数据类型]
Oracle 的字符串处理函数不如 Postgres 的函数强大(例如,没有 string_to_array 或 unnest),但您也可以在 PL/SQL 中实现类似的逻辑(尽管更复杂)
我正在尝试将逗号分隔的数字字符串与 sql 查询中的特定模式相匹配。我过去成功地使用正则表达式解决了类似的问题,所以我也试图让它们在这里工作。问题如下:
- 该字符串可以包含范围内的任何数字(例如 1-4)恰好 0-1 次。
- 两个数字以逗号分隔
- 数字必须按升序排列
(我认为这是一种没有放回的有序抽样的情况)
以 1-4 为例,以下条目应匹配:
1
1,2
1,3
1,4
1,2,3
1,2,4
1,3,4
1,2,3,4
2
2,3
2,4
3
3,4
4
这些不应该:
q dawda 323123 a3 a1 1aa,1234 4321 a4,32,1a 1112222334411
1,,2,33,444, 11,12,a 234 2,2,3 33 3,3,3 3,34 34 123 1,4,4,4a 1,444
我目前最好的尝试是:
\b[1-4][\,]?[2-4]?[\,]?[3-4]?[\,]?[4]?\b
这仍然有两个主要缺点:
- 它提供了很多误报。数字出现一次后不消除。
- 当数字范围增加时,它会变得相当长,例如1-18也已经可以了,可以考虑更大的范围。
我使用 regexpal 进行测试。
旁注:
- 因为我正在使用 sql,所以可以用另一种语言实现一些算法来生成所有可能的组合并将它们保存在可用于连接的 table 中,请参阅例如How to get all possible combinations of a list’s elements?。我只想依靠它作为最后的手段,因为将涉及新 table 的创建,并且这些将包含很多条目。
- 使用正则表达式的结果 sql 语句在 Postgres 和 Oracle 上应该 运行。
- 正例集也称为"powerset"。
编辑:澄清了正例列表
我不会为此使用正则表达式,例如要求 "have to be unique" 和 "have to be in ascending order" 不能真正用正则表达式表达(至少我想不出一种方法来做到这一点)。
由于您还需要有一个在 Postgres 和 Oracle 中相同的表达式,我会创建一个函数来检查这样的列表,然后在该函数中隐藏 DBMS 特定的实现。
对于 Postgres,我会使用其数组处理功能来实现该功能:
create or replace function is_valid(p_input text)
returns boolean
as
$$
select coalesce(array_agg(x order by x) = string_to_array(p_input, ','), false)
from (
select distinct x
from unnest(string_to_array(p_input,',')) as t(x)
where x ~ '^[0-9]+$' -- only numbers
) t
where x::int between 1 and 4 -- the cast is safe as the inner query only returns valid numbers
$$
language sql;
内部查询return将输入列表中的所有(不同)元素作为单独的数字。外部查询然后将其聚合回所需范围和数字顺序中的值。如果该结果与输入不同,则输入无效。
然后使用以下示例数据:
with sample_data (input) as (
values
('1'),
('1,2'),
('1,3'),
('1,4'),
('1,2,3'),
('1,2,4'),
('foo'),
('1aa,1234'),
('1,,2,33,444,')
)
select input, is_valid(input)
from sample_data;
会 return:
input | is_valid
-------------+---------
1 | true
1,2 | true
1,3 | true
1,4 | true
1,2,3 | true
1,2,4 | true
foo | false
1aa,1234 | false
1,,2,33,444, | false
如果您想在 Postgres 和 Oracle 中使用相同的函数,您可能需要在 Postgres 中使用 returns integer
,因为 Oracle 仍然不支持 SQL[=14= 中的布尔数据类型]
Oracle 的字符串处理函数不如 Postgres 的函数强大(例如,没有 string_to_array 或 unnest),但您也可以在 PL/SQL 中实现类似的逻辑(尽管更复杂)