0

我需要有关 Oracle 中正则表达式替换的帮助。我想替换文档中存在的所有单词或短语,只要该单词或短语不存在于一组标签中。标签由我定义(热 html 或 xml),我目前的概念是;

<term type=pos id=123>some phrase</term>

我为 regexp_replace 创建了一个函数包装器(非功能性),如下所示;

  FUNCTION ANNOTATE_ONE_TERM(IN_TEXT IN VARCHAR2, SEARCH_TERM IN VARCHAR2, TERM_TYPE IN VARCHAR2, RECORD_ID IN NUMBER) RETURN CLOB
  IS
    REGEX_SEARCH VARCHAR2(512);
    REGEX_REPLACE VARCHAR2(512);
  BEGIN
    REGEX_SEARCH := '((<TERM.*?</TERM>|[^<])*?)(^|\W)('|| SEARCH_TERM ||')($|\W)';
    REGEX_REPLACE := '\1 <TERM ID='|| TO_CHAR(RECORD_ID)||' TYPE=' || TERM_TYPE ||'>'|| SEARCH_TERM ||'</TERM> ';
    DBMS_OUTPUT.PUT_LINE('REGEX_SEARCH = ' || REGEX_SEARCH);
    DBMS_OUTPUT.PUT_LINE('REGEX_REPLACE = ' || REGEX_REPLACE);
    RETURN TRIM(REGEXP_REPLACE(IN_TEXT, REGEX_SEARCH, REGEX_REPLACE,1,0,'in'));
  END ANNOTATE_ONE_TERM;

当这样调用时;

SELECT ANNOTATE_ONE_TERM(
                         ANNOTATE_ONE_TERM('dog elephant dog cat cat dog dogfish fishdog mouse dog', 'DOG CAT', 'POS', 123),
                         'DOG', 'POS',456) 
FROM DUAL;

它返回;

<TERM ID=456 TYPE=POS>DOG</TERM> elephant <TERM ID=123 TYPE=POS>DOG CAT</TERM> cat <TERM ID=456 TYPE=POS>DOG</TERM> dogfish fishdog mouse <TERM ID=456 TYPE=POS>DOG</TERM>

哪个是对的。但是如果用这个调用;

SELECT ANNOTATE_ONE_TERM(
                         ANNOTATE_ONE_TERM('elephant dog cat cat dogfish fishdog mouse', 'DOG CAT', 'POS', 123),
                         'DOG', 'POS',456) 
FROM DUAL;

它返回;

elephant <TERM ID=123 TYPE=POS <TERM ID=456 TYPE=POS>DOG</TERM> CAT</TERM> cat dogfish fishdog mouse

这是错误的。它似乎正在吃“>”并在标签中找到单词/短语。

我正在积极尝试增加我对正则表达式的了解,但到目前为止,我一直没有意识到这一点。

4

1 回答 1

1

我了解您尝试“否定”匹配,我尝试仅使用结束标记进行直接匹配<\TERM>,这似乎有效:

create or replace FUNCTION ANNOTATE_ONE_TERM(IN_TEXT      IN VARCHAR2,
                                             SEARCH_TERM  IN VARCHAR2,
                                             TERM_TYPE    IN VARCHAR2,
                                             RECORD_ID    IN NUMBER)
RETURN CLOB IS
    REGEX_SEARCH  VARCHAR2(512);
    REGEX_REPLACE VARCHAR2(512);
BEGIN
    REGEX_SEARCH := '(?</TERM>| |^)' || SEARCH_TERM || '( |$)';
    REGEX_REPLACE := '\1<TERM ID=' || TO_CHAR(RECORD_ID) || ' TYPE=' 
                     || TERM_TYPE || '>' || SEARCH_TERM || '</TERM>\2';

    RETURN TRIM(REGEXP_REPLACE(IN_TEXT, REGEX_SEARCH, REGEX_REPLACE,1,0,'in'));
END ANNOTATE_ONE_TERM;

然后我们得到:

SELECT ANNOTATE_ONE_TERM(
           ANNOTATE_ONE_TERM('dog elephant dog cat cat dog dogfish fishdog mouse dog',
                             'DOG CAT', 'POS', 123),
           'DOG', 'POS',456) 
FROM DUAL;

给出:

<TERM ID=456 TYPE=POS>DOG</TERM> elephant <TERM ID=123 TYPE=POS>DOG CAT</TERM> cat <TERM ID=456 TYPE=POS>DOG</TERM> dogfish fishdog mouse <TERM ID=456 TYPE=POS>DOG</TERM>

SELECT ANNOTATE_ONE_TERM(
          ANNOTATE_ONE_TERM('elephant dog cat cat dogfish fishdog mouse',
                            'DOG CAT', 'POS', 123),
          'DOG', 'POS',456) 
FROM DUAL;

给出:

elephant <TERM ID=123 TYPE=POS>DOG CAT</TERM> cat dogfish fishdog mouse

正如预期的那样,没有交叉项。您必须使用这样的技巧,因为 Oracle 不支持前瞻/后瞻断言(至少在我的版本 11g 中)。

于 2013-09-04T15:23:31.310 回答