2017-05-28 137 views
1

我想从TOAD中使用SQL的表中的描述字段获取所有不可打印的ASCII字符,但是下面的查询不起作用。在SQL中显示不可打印的ascii字符ascii:或:print:不起作用

select 
regexp_instr(a.description,'[^[:ascii:]]') as description from 
poline a where a.ponum='XXX' and a.siteid='YYY' and 
regexp_instr(a.description,'[^[:ascii:]]') > 0 

上面的查询买了错误ORA-127729:正则表达式中的无效字符类。我试过:打印:而不是:ascii:但它没有带来任何结果。以下是对此记录具有不可打印字符的描述。

Sherlock 16 x 6.5” Wide Wheelbarrow wheel .M100P.10R – Effluent care bacteria and enzyme formulation 
+0

谢谢max092012。你是否希望每行都出现一个不可打印的字符,或者只是第一次出现? – alexgibbs

回答

1

:ascii:不是有效的字符类,即使它是,它并不显得你正在尝试到这里(ASCII不包含非打印字符)。可以找到有效的课程here

实际上,如果您在原始查询中将:ascii:替换为:print:,那么它确实会返回每个POLINE.DESCRIPTION中的第一个不可打印字符的位置。 (如果你没有返回,这可能是因为您的DESCRIPTION数据实际上是所有可打印的。)

但正如你所说,你要在POLINE识别非打印字符在每个DESCRIPTION,一些变化将需要。我会列举一个以每场比赛为起点的例子。

在此示例中,每个DESCRIPTION将被分解为其各个组成字符,并且将检查每个字符的可印刷性。将返回DESCRIPTION字符串中的位置以及不可打印字符的ASCII number

此示例假定POLINE中的每一行都有一个唯一标识符,这里称为POLINE_ID

首先,创建测试表:

CREATE TABLE POLINE(
    POLINE_ID NUMBER PRIMARY KEY, 
    PONUM VARCHAR2(32), 
    SITEID VARCHAR2(32), 
    DESCRIPTION VARCHAR2(256) 
); 

并加载一些数据。我在示例Sherlock字符串中插入了几个非打印字符,#23#17。还包括仅由前64个ASCII字符组成的示例字符串(其中前31个字符不在:print:中),以及一些填充符通过PONUMSITEID谓词。

INSERT INTO POLINE VALUES (1,'XXX','YYY','Sherlock'||CHR(23)||' 16 x 6.5” Wide Wheelbarrow wheel .M100P.10R –'||CHR(17)||' Effluent care bacteria and enzyme formulation'); 

DECLARE 
    V_STRING VARCHAR2(64) := CHR(1); 
BEGIN 
    FOR POINTER IN 2..64 LOOP 
    V_STRING := V_STRING||CHR(POINTER); 
    END LOOP; 
    INSERT INTO POLINE VALUES (2, 'XXX','YYY',V_STRING); 
    INSERT INTO POLINE VALUES (3, 'AAA','BBB',V_STRING); 
END; 
/

INSERT INTO POLINE VALUES(4,'XXX','YYY','VOLTRON'); 

现在我们共有4行。其中三个包含(多个)不可打印字符,但其中只有两个应匹配所有限制。

然后运行查询。下面有两个示例查询 - 第一个使用REGEXP_INSTR,与您在初始示例查询中一样(将:cntrl:替换为:print:)。但是,对于另一种选择,还包括第二个变体,它只检查每个字符是否在前31个ASCII字符中。

这两个示例查询都将索引每个DESCRIPTION的每个字符,并检查它是否可打印,并收集每个候选DESCRIPTION中每个不可打印字符的ASCII码和位置。这里的示例表具有长度为256个字符的DESCRIPTION s,所以这被用作笛卡尔连接中的最大索引。

请注意,这些都是效率不高,并设计让EVERY比赛。如果您最终只需要第一场比赛,您的原始查询替换为:print:将表现更好。此外,还可以通过放入PL/SOL或者递归(如果PL/SQL在您的用例中允许,或者您是11gR2 +等)来调整。此处的一些谓词如REGEXP_LIKE不会影响最终结果,仅用于初步过滤。根据您的数据集,这些对您来说可能是多余的(或更糟糕的)。

第一个例子,使用正则表达式和:print:

SELECT 
    POLINE_ID, 
    STRING_INDEX                  AS NON_PRINTABLE_LOCATION, 
    ASCII(REGEXP_SUBSTR(SUBSTR(DESCRIPTION, STRING_INDEX, 1), '[[:cntrl:]]', 1, 1)) AS NON_PRINTABLE_ASCII_NUMBER 
FROM POLINE 
    CROSS JOIN (SELECT LEVEL AS STRING_INDEX 
       FROM DUAL 
       CONNECT BY LEVEL < 257) CANDIDATE_LOCATION 
WHERE PONUM = 'XXX' 
     AND SITEID = 'YYY' 
     AND REGEXP_LIKE(DESCRIPTION, '[[:cntrl:]]') 
     AND REGEXP_INSTR(SUBSTR(DESCRIPTION, STRING_INDEX, 1), '[[:cntrl:]]', 1, 1, 0) > 0 
     AND STRING_INDEX <= LENGTH(DESCRIPTION) 
ORDER BY 1 ASC, 2 ASC; 

第二个例子,用ASCII码:

SELECT 
    POLINE_ID, 
    STRING_INDEX        AS NON_PRINTABLE_LOCATION, 
    ASCII(SUBSTR(DESCRIPTION, STRING_INDEX, 1)) AS NON_PRINTABLE_ASCII_NUMBER 
FROM POLINE 
    CROSS JOIN (SELECT LEVEL AS STRING_INDEX 
       FROM DUAL 
       CONNECT BY LEVEL < 257) CANDIDATE_LOCATION 
WHERE PONUM = 'XXX' 
     AND SITEID = 'YYY' 
     AND REGEXP_LIKE(DESCRIPTION, '[[:cntrl:]]') 
     AND ASCII(SUBSTR(DESCRIPTION, STRING_INDEX, 1)) BETWEEN 1 AND 31 
     AND STRING_INDEX <= LENGTH(DESCRIPTION) 
ORDER BY 1 ASC, 2 ASC; 

在我们的测试数据,这些查询会产生相同的输出。我们应该预计这将有两个命中(第17和23号)在SherlockDESCRIPTION和31命中的前64-ascii DESCRIPTION

结果:

POLINE_ID NON_PRINTABLE_LOCATION NON_PRINTABLE_ASCII_NUMBER 
1   9      23       
1   56      17       
2   1      1       
2   2      2       
2   3      3       
2   4      4       
2   5      5       
2   6      6       
2   7      7       
2   8      8       
2   9      9       
2   10      10       
2   11      11       
2   12      12       
2   13      13       
2   14      14       
2   15      15       
2   16      16       
2   17      17       
2   18      18       
2   19      19       
2   20      20       
2   21      21       
2   22      22       
2   23      23       
2   24      24       
2   25      25       
2   26      26       
2   27      27       
2   28      28       
2   29      29       
2   30      30       
2   31      31      

33 rows selected. 

编辑在回应评论,这里是什么,我们可以期待[[:cntrl:]][^[:cntrl:]]regexp_instr一些阐述。

[[:cntrl:]]将匹配任何第一31个ASCII字符,而[^[:cntrl:]][[:cntrl:]]逻辑非,所以除了第31个ASCII字符,它将匹配任何东西。
要比较这些,我们可以从最简单的情况开始,只有一个字符ascii #31。由于只有一个字符,结果只能是匹配或未命中。一会期待下返回1的比赛:

SELECT REGEXP_INSTR(CHR(31),'[[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL; 

MATCH_INDEX 
1 

但0的小姐否定[^ [:CNTRL:]:

SELECT REGEXP_INSTR(CHR(31),'[^[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL; 

MATCH_INDEX 
0 

现在,如果我们有两个(或更多)是可打印和不可打印的混合字符,因此会有更多可能的结果。 [[:cntrl:]][^[:cntrl:]]可以匹配,但他们只能匹配不同的东西。如果我们只从ascii #31移动到ascii #64#31,我们仍然期望[[:cntrl:]]匹配(因为在第二个位置有一个不可打印的字符),但现在应该返回2,因为不可打印位于第二个位置。

SELECT REGEXP_INSTR(CHR(64)||CHR(31),'[[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL; 

MATCH_INDEX 
2 

现在[^[:cntrl:]]有机会匹配(在第一位置):

SELECT REGEXP_INSTR(CHR(64)||CHR(31),'[^[:cntrl:]]',1,1,0) AS MATCH_INDEX FROM DUAL; 

MATCH_INDEX 
1 

当有可打印和控制字符的组合,既[[:cntrl:]][^[:cntrl:]]可以匹配,但他们会匹配不同的指数。

+0

非常感谢您的详细解释。我的要求是在描述字段中的任何位置选择不可打印的ascii字符。我遇到了一些测试,[^ [:cntrl:]]工作,但[[:cntrl:]] +也可以工作,但它们都带来不同的结果。你能否详细说明相同的区别?再次感谢!! – max092012

+0

谢谢@ max092012这将是几个小时(在我的时区后期),但我确定我会跟进[^ [:cntrl:]]和[[:cntrl:]]。由这些产生的有效匹配确实是不同的,所以他们产生不同的结果似乎是合理的。在你的情况下,我相信[:cntrl:]是你的目标。添加一些关于这些角色类的内容后,我会跟进。谢谢 – alexgibbs

+0

谢谢@ max092012我在帖子的底部添加了一个关于'regexp_instr'与'[[:cntrl:]]'vs'[^ [:cntrl:]]'对不同字符组合的行为的部分。这有助于澄清查询吗?谢谢 – alexgibbs