Sql Oracle-需要在给定字符串之间提取文本

Sql Oracle-需要在给定字符串之间提取文本,sql,regex,oracle,plsql,substring,Sql,Regex,Oracle,Plsql,Substring,示例-需要提取“开始”和“结束”之间的所有内容。我试着这样做: with phrases as ( select 'stackoverflow is awesome. Begin beginHello, World!End end It has everything!' as phrase from dual ) select regexp_replace(phrase , '([[:print:]]+Begin begin)([[:print:]]+)(

示例-需要提取“开始”和“结束”之间的所有内容。我试着这样做:

with phrases as (
  select 'stackoverflow 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 'stackoverflow 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
       ;
结果:

stackoverflow非常棒。开始吧,你好,世界!它已经结束了 一切

应该是:

你好,
世界

[编辑]

另一个问题。让我们看看这个示例:

WITH phrases AS (
  SELECT 'stackoverflow 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;
结果:

你好,
世界!它拥有一切

所以它匹配最后一次出现的结束字符串,这不是我想要的。子字符串应在我的标签的第一次出现时向外展开,因此结果应为:

你好,
世界

标签字符串首次出现后的所有内容都应忽略。有什么想法吗?

试试这个正则表达式:

([[:print:]]+Begin begin)(.+?)(End end[[:print:]]+)
示例用法:
点元字符(
)匹配数据库字符集中的任何字符和新行字符。但是,当调用regexp_replace时,match_参数必须包含
n
开关,用于
dot
匹配新行。

我不太熟悉POSIX
[[:print:]
字符类,但我使用通配符
实现了查询功能。您需要在
REGEXP\u REPLACE()
中指定
n
match参数,以便
可以匹配换行符:

WITH phrases AS (
  SELECT 'stackoverflow 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 'stackoverflow 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:]
应该与可打印字符匹配。它不匹配ASCII值低于32(空格)的任何内容。您需要使用

更新#2-每个问题更新一次-我认为正则表达式不会按您希望的方式工作。将惰性量词添加到
(.+)
没有效果,并且Oracle正则表达式没有前瞻性。您可以做几件事,一件是使用
INSTR()
SUBSTR()

另一种方法是将
INSTR()
SUBSTR()
与正则表达式组合:

WITH phrases AS (
  SELECT 'stackoverflow 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;

为了让第二个选项发挥作用,您需要添加
[:space:[:print:][]*
,如下所示:

with phrases as (
  select 'stackoverflow 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 'stackoverflow 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 'stackoverflow 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
       ;   
正则表达式的问题是,您可能必须确定变量的范围,并创建一个与所有变量匹配的规则。如果某些内容超出了您的范围,您必须访问regex并添加新的异常

你可以找到额外的信息


函数字段(i_字符串VARCHAR2
,i_分隔符VARCHAR2
,i_发生次数默认为1
,i_return_实例数默认值1)return VARCHAR2为
--
v_分隔符VARCHAR2(1);
n_end_位置编号;
n_开始位置编号:=1;
n_分隔符_位置编号;
n_seek_pos NUMBER:=1;
n_tbl_索引PLS_整数:=0;
n\u返回\u计数器编号:=0;
v_return_string VARCHAR2(32767);
tbl_类型是由PLS_整数表示的VARCHAR2(4000)索引表;
tbl tbl_型;
e_无分隔符例外;
v_string VARCHAR2(32767):=i_string | | i_分隔符;
开始
开始
环
----------------------------------------
--在中搜索分隔符
--串
----------------------------------------
n_分隔符位置:=instr(v_字符串,i_分隔符,n_seek_位置);
--
如果n_分隔符\u pos=length(v_字符串)和n_tbl_index=0,则
------------------------------------------
--您要查找的分隔符是
--不是在这个字符串中。
------------------------------------------
提出e_no_分隔符;
如果结束;
--
当n_分隔符_pos=0时退出;
n_start_pos:=n_seek_pos;
n_end_pos:=n_分隔符_pos-n_seek_pos;
n_seek_pos:=n_分隔符_pos+1;
--
n_tbl_索引:=n_tbl_索引+1;
-----------------------------------------------
--将字符串的段存储在tbl中
-----------------------------------------------
tbl(n_tbl_索引):=substr(i_字符串,n_开始位置,n_结束位置);
端环;
----------------------------------------------
--为返航准备结果
----------------------------------------------
v_分隔符:=NULL;
对于tbl中的第一个。。最后一个循环
如果>=i\u发生且n\u返回\u计数器
它是如何失败的?在哪里可以放入
\n
并打破它?我更新了我的问题-有趣的问题。我不能想出解决办法,但我在看谁能想出。:)虽然Stephan和David Faber有一个很好的解决方案,但看看其他人是如何解决新行字符的变化的,因为它与Oracle中的正则表达式有关。我发现在stackoverflow.com/questions/16407135/…这里可以看到@APC是如何做到这一点的。仍然返回整个字符串。“n”实际上是忽略新行的键。我实际上不想忽略新行。我需要在
with phrases as (
  select 'stackoverflow 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 'stackoverflow 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
       ;   
 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;