在SQL中显示不可打印的ascii字符:ascii:或:print:不起作用

时间:2017-05-28 14:31:26

标签: sql oracle oracle11g toad

我正在尝试使用TOAD中的SQL从表中的DESCRIPTION字段获取所有不可打印的ASCII字符,但是以下查询无法正常工作。

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:正则表达式中的字符类无效。我试过:print:而不是:ascii:但它没有带来任何结果。以下是此记录的说明,其中包含不可打印的字符。

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

1 个答案:

答案 0 :(得分:1)

:ascii:不是有效的字符类,即使它是,它似乎也不是你想要到达的字符(ascii包含不可打印的字符)。可以找到有效的类here

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

但正如您所说,您希望在DESCRIPTION中的每个POLINE中识别 每个 不可打印的字符,则需要进行一些更改。我将包含一个将每场比赛作为首发位置的示例。

在此示例中,每个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:)。但是对于替代方案,还包括第二个变体,它只检查每个char是否在前31个ascii字符中。

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

请注意,这些 效率不高 ,旨在让每次匹配。如果你最终只需要第一场比赛,那么用: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;

在我们的测试数据中,这些查询将产生等效的输出。我们应该期望Sherlock DESCRIPTION有两次点击(第17和23章),第一次64-ascii DESCRIPTION有31次点击。

结果:

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:]]都可以匹配,但它们会匹配不同的索引。