【问题标题】:Split string by space and character as delimiter in Oracle with regexp_substr使用 regexp_substr 在 Oracle 中按空格和字符拆分字符串作为分隔符
【发布时间】:2015-10-17 17:11:44
【问题描述】:

我正在尝试使用 regexp_subtr 拆分字符串,但无法使其正常工作。

所以,首先,我有这个查询

select regexp_substr('Helloworld - test!' ,'[[:space:]]-[[:space:]]') from dual

很好地提取了我的分隔符 - blank-blank

但是,当我尝试使用此选项拆分字符串时,它就不起作用了。

select regexp_substr('Helloworld - test!' ,'[^[[:space:]]-[[:space:]]]+')from dual

查询不返回任何内容。

我们将不胜感激! 谢谢

【问题讨论】:

  • 你的预期输出是什么?只有分隔符?
  • 嗯,结果应该是“Helloworld”。但重要的是分隔符将是“ - ”,因为我想用“select regexp_substr('Helloworld - test!','[^[[:space:]]-[[ :space:]]]+',1,2)"
  • 哎呀抱歉..我没有动静。你想得到分隔符之前的值还是分隔符之后的值??
  • 好的,所以我有这个查询,以及我想要的结果。 "select regexp_substr('Helloworld - test!' ,'[^[[:space:]]-[[:space:]]]+'), select regexp_substr('Helloworld - test!' ,'[^[[: space:]]-[[:space:]]]+',1,2) from dual”,结果应该是“Helloworld”作为第 1 列,“test”作为第 2 列。我的问题是我不知道如何将分隔符设置为“空格 - 空格”。如果我只使用“-”,它就可以完美地工作,但我真的需要“空格”作为分隔符。例如“select regexp_substr('Helloworld - test!','[^-]+')from dual”工作得很好,但我想在分隔符中添加空格(空白)

标签: sql regex oracle string-split regexp-substr


【解决方案1】:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE TEST( str ) AS
          SELECT 'Hello world - test-test! - test' FROM DUAL
UNION ALL SELECT 'Hello world2 - test2 - test-test2' FROM DUAL;

查询 1

SELECT Str,
       COLUMN_VALUE AS Occurrence,
       REGEXP_SUBSTR( str ,'(.*?)([[:space:]]-[[:space:]]|$)', 1, COLUMN_VALUE, NULL, 1 ) AS split_value
FROM   TEST,
       TABLE(
         CAST(
           MULTISET(
             SELECT LEVEL
             FROM   DUAL
             CONNECT BY LEVEL < REGEXP_COUNT( str ,'(.*?)([[:space:]]-[[:space:]]|$)' )
           )
           AS SYS.ODCINUMBERLIST
         )
       )

Results

|                               STR | OCCURRENCE |  SPLIT_VALUE |
|-----------------------------------|------------|--------------|
|   Hello world - test-test! - test |          1 |  Hello world |
|   Hello world - test-test! - test |          2 |   test-test! |
|   Hello world - test-test! - test |          3 |         test |
| Hello world2 - test2 - test-test2 |          1 | Hello world2 |
| Hello world2 - test2 - test-test2 |          2 |        test2 |
| Hello world2 - test2 - test-test2 |          3 |   test-test2 |

【讨论】:

    【解决方案2】:

    如果我理解正确,这将对您有所帮助。目前您的输出为Helloworld(末尾有空格)。所以我假设你不想在最后有空间。如果是这样,您也可以简单地使用分隔符中的空格。

    select regexp_substr('Helloworld - test!' ,'[^ - ]+',1,1)from dual;
    
    OUTPUT
    Helloworld(No space at the end)
    

    正如你在评论中提到的,如果你想用Helloworldtest! 输出两列。您可以执行以下操作。

    select regexp_substr('Helloworld - test!' ,'[^ - ]+',1,1),
           regexp_substr('Helloworld - test!' ,'[^ - ]+',1,3) from dual;
    
    OUTPUT
    col1         col2
    Helloworld   test!
    

    【讨论】:

    • 是的,它有点工作,但有一个问题:如果字符串是“Hello World - test”(Hello 和 World 之间有空格),那么 column1 得到“Hello”,第二列得到“ ——”。它用它找到的第一个空格来分割字符串,而不是空格。可能一开始我不是很清楚,抱歉。所以如果我的字符串是“Hello World - test”,结果应该是“Hello World”和“test” :) 非常感谢你的努力!!!
    【解决方案3】:

    尝试通过将匹配字符串'[[:space:]]-[[:space:]]' 置于带有抑扬符 (^) 的字符类中来否定匹配字符串,这是行不通的。一对方括号之间的所有内容都被视为可选单个字符的列表,但命名的命名字符类除外,它们扩展为可选字符列表,但是,由于字符类的嵌套方式,您的外括号很可能是解释如下:

    • [^[[:space:]] 单个非空格非左方括号字符
    • - 后跟一个连字符
    • [[:space:]] 后跟一个空格字符
    • ]+ 后跟 1 个或多个右方括号。

    使用 regexp_replace 将多字符分隔符转换为单个字符可能会更容易,然后使用 regex_substr 来查找各个部分:

    select regexp_substr(regexp_replace('Helloworld - test!'
                                       ,'[[:space:]]-[[:space:]]'
                                       ,chr(11))
                        ,'([^'||chr(11)||']*)('||chr(11)||'|$)'
                        ,1 -- Start here
                        ,2 -- return 1st, 2nd, 3rd, etc. match
                        ,null
                        ,1 -- return 1st sub exp
                        )
      from dual;
    

    在这段代码中,我首先将- 更改为chr(11)。这是 ASCII 垂直制表符 (VT) 字符,它不太可能出现在大多数文本字符串中。然后 regexp_substr 的匹配表达式匹配所有非 VT 字符,后跟一个 VT 字符或行尾。仅返回非 VT 字符(第一个子表达式)。

    【讨论】:

      【解决方案4】:

      MT0 的回答略有改进。使用 regexp_count 的动态计数并证明它可以处理 [^delimiter]+ 作为模式的格式不处理 NULL 列表元素的空值。更多信息在这里:Split comma seperated values to columns

      SQL> with tbl(str) as (
        2    select ' - Hello world - test-test! -  - test - ' from dual
        3  )
        4  SELECT LEVEL AS Occurrence,
        5         REGEXP_SUBSTR( str ,'(.*?)([[:space:]]-[[:space:]]|$)', 1, LEVEL, NULL, 1 ) AS split_value
        6  FROM   tbl
        7  CONNECT BY LEVEL <= regexp_count(str, '[[:space:]]-[[:space:]]')+1;
      
      OCCURRENCE SPLIT_VALUE
      ---------- ----------------------------------------
               1
               2 Hello world
               3 test-test!
               4
               5 test
               6
      
      6 rows selected.
      
      SQL>
      

      【讨论】:

      • 在主查询中使用 CONNECT BY 对于单行输入可以正常工作,但如果您尝试对多行输入执行该查询,则会给出错误结果。我已经用支持多行的版本更新了我的答案。
      【解决方案5】:
      CREATE OR REPLACE FUNCTION field(i_string            VARCHAR2
                                      ,i_delimiter         VARCHAR2
                                      ,i_occurance         NUMBER
                                      ,i_return_number     NUMBER DEFAULT 0
                                      ,i_replace_delimiter VARCHAR2) RETURN VARCHAR2     IS
        -----------------------------------------------------------------------
        -- Function Name.......: FIELD
        -- Author..............: Dan Simson
        -- Date................: 05/06/2016 
        -- Description.........: This function is similar to the one I used from 
        --                       long ago by Prime Computer.  You can easily
        --                       parse a delimited string.
        -- Example.............: 
        --  String.............: This is a cool function
        --  Delimiter..........: ' '
        --  Occurance..........: 2
        --  Return Number......: 3
        --  Replace Delimiter..: '/'
        --  Return Value.......: is/a/cool
        --------------------------------------------------------------------------    ---                                    
        v_return_string  VARCHAR2(32767);
        n_start          NUMBER := i_occurance;
        v_delimiter      VARCHAR2(1);
        n_return_number  NUMBER := i_return_number;
        n_max_delimiters NUMBER := regexp_count(i_string, i_delimiter);
      BEGIN
        IF i_return_number > n_max_delimiters THEN
          n_return_number := n_max_delimiters + 1;
        END IF;
        FOR a IN 1 .. n_return_number LOOP
          v_return_string := v_return_string || v_delimiter || regexp_substr    (i_string, '[^' || i_delimiter || ']+', 1, n_start);
          n_start         := n_start + 1;
          v_delimiter     := nvl(i_replace_delimiter, i_delimiter);
        END LOOP;
        RETURN(v_return_string);
      END field;
      
      
      SELECT field('This is a cool function',' ',2,3,'/') FROM dual;
      
      SELECT regexp_substr('This is a cool function', '[^ ]+', 1, 1) Word1
            ,regexp_substr('This is a cool function', '[^ ]+', 1, 2) Word2
            ,regexp_substr('This is a cool function', '[^ ]+', 1, 3) Word3
            ,regexp_substr('This is a cool function', '[^ ]+', 1, 4) Word4
            ,regexp_substr('This is a cool function', '[^ ]+', 1, 5) Word5
        FROM dual;
      

      【讨论】:

      • 一些解释会很好。
      • 我经常需要解析一些文本以提取文本的子集。有时文本有分隔符,例如句点: The.File.Name.is.THIS.created.on.20160506 我真正想要的只是这个词。我可以使用以下语句调用我的 FIELD 函数:SELECT field('The.File.Name.is.THIS.created.on.20160506','.',5,1) FROM dual;
      • 抓起函数试试看。您可以使用它轻松解析任何文本。我查看了这篇文章中的一些很好的例子。它们都工作得很好,但我永远无法记住所有那些疯狂的正则表达式点点滴滴:'([^'||chr(11)||']*)('||chr(11)||' |$)'
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多