Oracle - 需要在给定字符串之间提取文本
Posted
技术标签:
【中文标题】Oracle - 需要在给定字符串之间提取文本【英文标题】:Oracle - need to extract text between given strings 【发布时间】:2015-04-24 20:02:56 【问题描述】:示例 - 需要提取“Begin begin”和“End end”之间的所有内容。我试过这样:
with phrases as (
select '*** is awesome. Begin beginHello, World!End end It has everything!' as phrase
from dual
)
select regexp_replace(phrase
, '([[:print:]]+Begin begin)([[:print:]]+)(End end[[:print:]]+)', '\2')
from phrases
;
结果:你好,世界!
但是,如果我的文本包含换行符,它会失败。任何提示如何解决此问题以允许提取包含新行的文本?
[编辑]它是如何失败的:
with phrases as (
select '*** is awesome. Begin beginHello,
World!End end It has everything!' as phrase
from dual
)
select regexp_replace(phrase
, '([[:print:]]+Begin begin)([[:print:]]+)(End end[[:print:]]+)', '\2')
from phrases
;
结果:
*** 很棒。开始 beginHello, World!End end 它有 一切!
应该是:
你好, 世界!
[编辑]
另一个问题。让我们看看这个示例:
WITH phrases AS (
SELECT '*** is awesome. Begin beginHello,
World!End end It has everything!End endTESTESTESTES' AS phrase
FROM dual
)
SELECT REGEXP_REPLACE(phrase, '.+Begin begin(.+)End end.+', '\1', 1, 1, 'n')
FROM phrases;
结果:
你好, World!End end 它拥有一切!
所以它匹配最后出现的结束字符串,这不是我想要的。子字符串应该被提取到我的标签的第一次出现,所以结果应该是:
你好, 世界!
第一次出现标签字符串之后的所有内容都应该被忽略。有什么想法吗?
【问题讨论】:
它是如何失败的?你可以在哪里输入\n
并打破它?
我更新了我的问题
有趣的问题。我无法找出解决方案,但我正在关注谁会这样做。 :)
虽然 Stephan 和 David Faber 有一个很好的解决方案,但值得看看其他人如何解决一般的换行符变体,因为它与 Oracle 中的正则表达式有关。我发现在这里查看@APC 是如何做到这一点的,***.com/questions/16407135/...
【参考方案1】:
我对 POSIX [[:print:]]
字符类不太熟悉,但我使用通配符 .
使您的查询正常工作。您需要在REGEXP_REPLACE()
中指定n
匹配参数,以便.
可以匹配换行符:
WITH phrases AS (
SELECT '*** is awesome. Begin beginHello,
World!End end It has everything!' AS phrase
FROM dual
)
SELECT REGEXP_REPLACE(phrase, '.+Begin begin(.+)End end.+', '\1', 1, 1, 'n')
FROM phrases;
我使用了\1
反向引用,因为我没有看到需要从正则表达式中捕获其他组。 如果分隔符前后没有任何内容,使用*
量词(而不是+
)也是一个好主意。如果您想捕获所有组,那么您可以使用以下内容:
WITH phrases AS (
SELECT '*** is awesome. Begin beginHello,
World!End end It has everything!' AS phrase
FROM dual
)
SELECT REGEXP_REPLACE(phrase, '(.+Begin begin)(.+)(End end.+)', '\2', 1, 1, 'n')
FROM phrases;
更新 - 仅供参考,我使用 [[:print:]]
进行了测试,但它不起作用。这并不奇怪,因为 [[:print:]]
应该匹配 printable 字符。它不匹配任何低于 32(空格)的 ASCII 值。您需要使用.
。
更新 #2 - 每次更新问题 - 我认为正则表达式不会按您希望的方式工作。将惰性量词添加到 (.+)
没有任何效果,Oracle 正则表达式没有前瞻功能。您可能会做几件事,其中之一是使用INSTR()
和SUBSTR()
:
WITH phrases AS (
SELECT '*** is awesome. Begin beginHello,
World!End end It has everything!End endTESTTESTTEST' AS phrase
FROM dual
)
SELECT SUBSTR(phrase, str_start, str_end - str_start) FROM (
SELECT INSTR(phrase, 'Begin begin') + LENGTH('Begin begin') AS str_start
, INSTR(phrase, 'End end') AS str_end, phrase
FROM phrases
);
另一种是将INSTR()
和SUBSTR()
与正则表达式结合起来:
WITH phrases AS (
SELECT '*** is awesome. Begin beginHello,
World!End end It has everything!End endTESTTESTTEST' AS phrase
FROM dual
)
SELECT REGEXP_REPLACE(SUBSTR(phrase, 1, INSTR(phrase, 'End end') + LENGTH('End end')), '.+Begin begin(.+)End end.+', '\1', 1, 1, 'n')
FROM phrases;
【讨论】:
'n' 实际上是忽略新行的关键 我实际上不想忽略新行。我需要在已知字符串之间提取未触及的文本。 好的,我刚刚注意到你的灵魂工作是我需要的,结果字符串中有新行,【参考方案2】:试试这个正则表达式:
([[:print:]]+Begin begin)(.+?)(End end[[:print:]]+)
示例用法:
SELECT regexp_replace(
phrase ,
'([[:print:]]+Begin begin)(.+?)(End end[[:print:]]+)',
'\2',
1, -- Start at the beginning of the phrase
0, -- Replace ALL occurences
'n' -- Let dot meta character matches new line character
)
FROM
(SELECT '*** is awesome. Begin beginHello, '
|| chr(10)
|| ' World!End end It has everything!' AS phrase
FROM DUAL
)
点元字符 (.
) 匹配数据库字符集中的任何字符和换行符。但是,当调用 regexp_replace 时,match_parameter 必须包含 n
开关,以便 dot
匹配新行。
【讨论】:
没有变化。仍然返回整个字符串。【参考方案3】:为了让您的第二个选项起作用,您需要添加[[:space:][:print:]]*
,如下所示:
with phrases as (
select '*** is awesome. Begin beginHello,
World!End end It has everything!' as phrase
from dual
)
select regexp_replace(phrase
, '([[:print:]]+Begin begin)([[:print:]]+[[:space:][:print:]]*)(End end[[:print:]]+)', '\2')
from phrases
;
但如果你有更多\n
,它仍然会中断,例如它不会工作
with phrases as (
select '*** is awesome. Begin beginHello,
World!End end
It has everything!' as phrase
from dual
)
select regexp_replace(phrase
, '([[:print:]]+Begin begin)([[:print:]]+[[:space:][:print:]]*)(End end[[:print:]]+)', '\2')
from phrases
;
那你需要添加
with phrases as (
select '*** is awesome. Begin beginHello,
World!End end
It has everything!' as phrase
from dual
)
select regexp_replace(phrase
, '([[:print:]]+Begin begin)([[:print:]]+[[:space:][:print:]]*)(End end[[:print:]]+[[:space:][:print:]]*)', '\2')
from phrases
;
正则表达式的问题在于,您可能必须确定变体的范围并创建一个匹配所有变体的规则。如果某些内容超出了您的范围,则必须访问正则表达式并添加新的异常。
您可以找到更多信息here。
【讨论】:
我有大文本,它可以包含许多新行。我需要从中提取子字符串。那么有没有通用的解决方案呢? 通常通过在每个可以找到新行的潜在位置添加[[:space:][:print:]]*
会有所帮助,您对这一行的意思是“您可能会找到一个空格或新行和字母”这就是*
的用途。【参考方案4】:
Description.........: This is a function similar to the one that was available from PRIME Computers
back in the late 80/90's. This function will parse out a segment of a string
based on a supplied delimiter. The delimiters can be anything.
Usage:
Field(i_string =>'This.is.a.cool.function'
,i_deliiter => '.'
,i_start_pos => 2
,i_occurrence => 2)
Return value = is.a
FUNCTION field(i_string VARCHAR2
,i_delimiter VARCHAR2
,i_occurance NUMBER DEFAULT 1
,i_return_instances NUMBER DEFAULT 1) RETURN VARCHAR2 IS
--
v_delimiter VARCHAR2(1);
n_end_pos NUMBER;
n_start_pos NUMBER := 1;
n_delimiter_pos NUMBER;
n_seek_pos NUMBER := 1;
n_tbl_index PLS_INTEGER := 0;
n_return_counter NUMBER := 0;
v_return_string VARCHAR2(32767);
TYPE tbl_type IS TABLE OF VARCHAR2(4000) INDEX BY PLS_INTEGER;
tbl tbl_type;
e_no_delimiters EXCEPTION;
v_string VARCHAR2(32767) := i_string || i_delimiter;
BEGIN
BEGIN
LOOP
----------------------------------------
-- Search for the delimiter in the
-- string
----------------------------------------
n_delimiter_pos := instr(v_string, i_delimiter, n_seek_pos);
--
IF n_delimiter_pos = length(v_string) AND n_tbl_index = 0 THEN
------------------------------------------
-- The delimiter you are looking for is
-- not in this string.
------------------------------------------
RAISE e_no_delimiters;
END IF;
--
EXIT WHEN n_delimiter_pos = 0;
n_start_pos := n_seek_pos;
n_end_pos := n_delimiter_pos - n_seek_pos;
n_seek_pos := n_delimiter_pos + 1;
--
n_tbl_index := n_tbl_index + 1;
-----------------------------------------------
-- Store the segments of the string in a tbl
-----------------------------------------------
tbl(n_tbl_index) := substr(i_string, n_start_pos, n_end_pos);
END LOOP;
----------------------------------------------
-- Prepare the results for return voyage
----------------------------------------------
v_delimiter := NULL;
FOR a IN tbl.first .. tbl.last LOOP
IF a >= i_occurance AND n_return_counter < i_return_instances THEN
v_return_string := v_return_string || v_delimiter || tbl(a);
v_delimiter := i_delimiter;
n_return_counter := n_return_counter + 1;
END IF;
END LOOP;
--
EXCEPTION
WHEN e_no_delimiters THEN
v_return_string := i_string;
END;
RETURN TRIM(v_return_string);
END;
【讨论】:
以上是关于Oracle - 需要在给定字符串之间提取文本的主要内容,如果未能解决你的问题,请参考以下文章