【问题标题】:RegEx split string with on a delimeter(semi-colon ;) except those that appear inside a stringRegEx 使用分隔符(分号;)拆分字符串,但出现在字符串中的字符串除外
【发布时间】:2011-11-17 19:32:36
【问题描述】:

我有一个 Java 字符串,它实际上是一个 SQL 脚本。

CREATE OR REPLACE PROCEDURE Proc
   AS
        b NUMBER:=3;
        c VARCHAR2(2000);
    begin
        c := 'BEGIN ' || ' :1 := :1 + :2; ' || 'END;';
   end Proc;

我想用分号分割脚本,除了出现在字符串中的那些。 所需的输出是四个不同的字符串,如下所述

1- CREATE OR REPLACE PROCEDURE Proc AS b NUMBER:=3
2- c VARCHAR2(2000)
3- begin c := 'BEGIN ' || ' :1 := :1 + :2; ' || 'END;';
4- end Proc

Java Split() 方法也会将上面的字符串拆分为标记。我想保留这个字符串,因为分号在引号内。

c := 'BEGIN ' || ' :1 := :1 + :2; ' || 'END;';

Java Split() 方法输出

1- c := 'BEGIN ' || ' :1 := :1 + :2
2- ' || 'END
3- '

请建议一个正则表达式,它可以用分号分割字符串,除了那些在字符串中的。

====================== CASE-2 ==================== ====

以上部分已得到解答,其工作原理

这是另一个更复杂的案例

============================================== ==========

我有一个 SQL 脚本,我想标记每个 SQL 查询。每个 SQL 查询由分号 (;) 或正斜杠 (/) 分隔。

1- 如果分号或 / 出现在类似的字符串中,我想转义它们

...WHERE col1 = 'some ; name/' ..

2- 表达式还必须转义任何多行注释语法,即 /*

这是输入

/*Query 1*/
SELECT
*
FROM  tab t
WHERE (t.col1 in (1, 3)
            and t.col2 IN (1,5,8,9,10,11,20,21,
                                     22,23,24,/*Reaffirmed*/
                                     25,26,27,28,29,30,
                                     35,/*carnival*/
                                     75,76,77,78,79,
                                     80,81,82, /*Damark accounts*/
                                     84,85,87,88,90))
;
/*Query 2*/    
select * from table
/
/*Query 3*/
select col form tab2
;
/*Query 4*/
select col2 from tab3 /*this is a multi line comment*/
/

想要的结果

[1]: /*Query 1*/
    SELECT
    *
    FROM  tab t
    WHERE (t.col1 in (1, 3)
                and t.col2 IN (1,5,8,9,10,11,20,21,
                                         22,23,24,/*Reaffirmed*/
                                         25,26,27,28,29,30,
                                         35,/*carnival*/
                                         75,76,77,78,79,
                                         80,81,82, /*Damark accounts*/
                                         84,85,87,88,90))

[2]:/*Query 2*/    
    select * from table

[3]: /*Query 3*/
    select col form tab2

[4]:/*Query 4*/
    select col2 from tab3 /*this is a multi line comment*/

其中一半已经可以通过上一篇文章中向我提出的建议来实现(link a start)但是当 cmets 语法(/*)被引入到查询中并且每个查询也可以用正斜杠分隔时(/ ),表达式不起作用。

【问题讨论】:

  • 转义出现在字符串文字中的引号的规则是什么?
  • 从文件中读取整个脚本并存储在字符串中。
  • 有趣的是,有一个相关的问题(#2)实际上和你的几乎一模一样……见:stackoverflow.com/questions/328387/…
  • 有人能看看这个场景吗?和 / 如果它们出现在字符串或单个或块注释(/* 或 --)中,则转义它们。示例:codesel * from tab;sfasdf

标签: java regex string stringtokenizer


【解决方案1】:

正则表达式模式((?:(?:'[^']*')|[^;])*); 应该可以满足您的需求。使用while 循环和Matcher.find() 提取所有SQL 语句。比如:

Pattern p = Pattern.compile("((?:(?:'[^']*')|[^;])*);";);
Matcher m = p.matcher(s);
int cnt = 0;
while (m.find()) {
    System.out.println(++cnt + ": " + m.group(1));
}

使用您提供的示例 SQL,将输出:

1: CREATE OR REPLACE PROCEDURE Proc
   AS
        b NUMBER:=3
2: 
        c VARCHAR2(2000)
3: 
    begin
        c := 'BEGIN ' || ' :1 := :1 + :2; ' || 'END;'
4: 
   end Proc

如果您想获得终止的;,请使用m.group(0) 而不是m.group(1)。

有关正则表达式的更多信息,请参阅Pattern JavaDoc 和this great reference。以下是该模式的概要:

(              Start capturing group
  (?:          Start non-capturing group
    (?:        Start non-capturing group
      '        Match the literal character '
      [^']     Match a single character that is not '
      *        Greedily match the previous atom zero or more times
      '        Match the literal character '
    )          End non-capturing group
    |          Match either the previous or the next atom
    [^;]       Match a single character that is not ;
  )            End non-capturing group
  *            Greedily match the previous atom zero or more times
)              End capturing group
;              Match the literal character ;

【讨论】:

  • 你能解释一下这个模式吗,因为我很难理解它。
  • (?:'[^']*') = 非捕获组匹配开始引用到结束引用
  • [^;] = 不是分号的单个字符
  • (?: (?: '[^']*') | [^;])* = 匹配不包含在引号中的第一个分号之前的所有内容的非捕获组
  • ((?:(?:'[^']*')|[^;])*); = 捕获所有内容,包括第一个分号,而不是引号
【解决方案2】:

您可以尝试的只是拆分“;”。然后对于每个字符串,如果它有奇数个 's,则将其与以下字符串连接,直到它有偶数个 's 添加“;”s。

【讨论】:

  • 这就是我已经做过的,但我试图找到一些简单的东西。感谢您的建议
  • 上述策略对以下语句失败。但是可以通过查看转义字符来修复它 select 'hello \';世界';
【解决方案3】:

我遇到了同样的问题。我看到了以前的建议并决定改进以下方面的处理:

  • 评论
  • 转义单引号
  • 不以分号结尾的单个查询

我的解决方案是为 java 编写的。反斜杠转义和 DOTALL 模式等某些内容可能会从一种语言更改为另一种语言。

这对我有用"(?s)\s*((?:'(?:\\.|[^\\']|''|)<em>'|/\</em>.*?\*/|(?:--|#)[^\r\n]<em>|[^\\'])</em>?)(?:;|$)"

"
(?s)                 DOTALL mode. Means the dot includes \r\n
\\s*                 Initial whitespace
(
    (?:              Grouping content of a valid query
        '            Open string literal
        (?:          Grouping content of a string literal expression
            \\\\.    Any escaped character. Doesn't matter if it's a single quote
        |
            [^\\\\'] Any character which isn't escaped. Escaping is covered above.
        |
            ''       Escaped single quote
        )            Any of these regexps are valid in a string literal.
        *            The string can be empty 
        '            Close string literal
    |
        /\\*         C-style comment start
        .*?          Any characters, but as few as possible (doesn't include */)
        \\*/         C-style comment end
    |
        (?:--|#)     SQL comment start
        [^\r\n]*     One line comment which ends with a newline
    |
        [^\\\\']     Anything which doesn't have to do with a string literal
    )                Theses four tokens basically define the contents of a query
    *?               Avoid greediness of above tokens to match the end of a query
)
(?:;|$)              After a series of query tokens, find ; or EOT
"

至于您的第二种情况,请注意正则表达式的最后一部分表示您的正则表达式将如何结束。现在它只接受分号或文本结尾。但是,您可以在结尾添加任何您想要的内容。例如(?:;|@|/|$) 接受 at 和 slash 作为结束字符。尚未为您测试此解决方案,但应该不难。

【讨论】:

    猜你喜欢
    • 2015-12-28
    • 1970-01-01
    • 2014-06-29
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 2018-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多