我们目前正在将我们的一个oracle数据库迁移到UTF8,并且我们发现了一些接近4000字节varchar限制的记录。 当我们尝试迁移这些记录时,它们会失败,因为它们包含成为多字节UF8字符的字符。 我想在PL / SQL中做的是找到这些字符以查看它们是什么,然后更改它们或删除它们。
我想这样做:
SELECT REGEXP_REPLACE(COLUMN,'[^[:ascii:]],'')
但是Oracle没有实现[:ascii:]字符类。
有没有一种简单的方法可以做我想做的事情?
答案 0 :(得分:22)
如果您使用ASCIISTR
函数将Unicode转换为\nnnn
格式的文字,则可以使用REGEXP_REPLACE
删除这些文字,就像这样......
UPDATE table SET field = REGEXP_REPLACE(ASCIISTR(field), '\\[[:xdigit:]]{4}', '')
...其中field和table分别是你的字段和表名。
答案 1 :(得分:21)
我认为这样可以解决问题:
SELECT REGEXP_REPLACE(COLUMN, '[^[:print:]]', '')
答案 2 :(得分:9)
我不推荐它用于生产代码,但它有意义并且似乎有效:
SELECT REGEXP_REPLACE(COLUMN,'[^' || CHR(1) || '-' || CHR(127) || '],'')
答案 3 :(得分:7)
在单字节ASCII兼容编码(例如Latin-1)中,ASCII字符只是0到127范围内的字节。因此,您可以使用类似[\x80-\xFF]
的内容来检测非ASCII字符。 / p>
答案 4 :(得分:4)
选择可能如下所示:
select nvalue from table
where length(asciistr(nvalue))!=length(nvalue)
order by nvalue;
答案 5 :(得分:3)
使用正则表达式可能有更直接的方法。幸运的是,其他人会提供它。但这就是我要做的事情而不需要去手册。
创建一个PLSQL函数来接收输入字符串并返回一个varchar2。
在PLSQL函数中,执行输入的asciistr()。 PLSQL是因为它可能返回一个长于4000的字符串,并且在PLSQL中你有32K可用于varchar2。
该函数将非ASCII字符转换为\ xxxx表示法。因此,您可以使用正则表达式来查找和删除它们。然后返回结果。
答案 6 :(得分:3)
以下也有效:
select dump(a,1016), a from (
SELECT REGEXP_REPLACE (
CONVERT (
'3735844533120%$03 ',
'US7ASCII',
'WE8ISO8859P1'),
'[^!@/\.,;:<>#$%&()_=[:alnum:][:blank:]]') a
FROM DUAL);
答案 7 :(得分:2)
尝试以下方法:
-- To detect
select 1 from dual
where regexp_like(trim('xx test text æ¸¬è© ¦ “xmx” number²'),'['||chr(128)||'-'||chr(255)||']','in')
-- To strip out
select regexp_replace(trim('xx test text æ¸¬è© ¦ “xmxmx” number²'),'['||chr(128)||'-'||chr(255)||']','',1,0,'in')
from dual
答案 8 :(得分:2)
我在这里找到了答案:
http://www.squaredba.com/remove-non-ascii-characters-from-a-column-255.html
CREATE OR REPLACE FUNCTION O1DW.RECTIFY_NON_ASCII(INPUT_STR IN VARCHAR2)
RETURN VARCHAR2
IS
str VARCHAR2(2000);
act number :=0;
cnt number :=0;
askey number :=0;
OUTPUT_STR VARCHAR2(2000);
begin
str:=’^'||TO_CHAR(INPUT_STR)||’^';
cnt:=length(str);
for i in 1 .. cnt loop
askey :=0;
select ascii(substr(str,i,1)) into askey
from dual;
if askey < 32 or askey >=127 then
str :=’^'||REPLACE(str, CHR(askey),”);
end if;
end loop;
OUTPUT_STR := trim(ltrim(rtrim(trim(str),’^'),’^'));
RETURN (OUTPUT_STR);
end;
/
然后运行此更新数据
update o1dw.rate_ipselect_p_20110505
set NCANI = RECTIFY_NON_ASCII(NCANI);
答案 9 :(得分:2)
我有一个类似的问题,并在博客上发表了here。 我开始使用alpha数字的正则表达式,然后在我喜欢的几个基本标点字符中添加:
select dump(a,1016), a, b
from
(select regexp_replace(COLUMN,'[[:alnum:]/''%()> -.:=;[]','') a,
COLUMN b
from TABLE)
where a is not null
order by a;
我使用了1016变体的dump来给出我想要替换的十六进制字符,然后我可以在utl_raw.cast_to_varchar2中使用。
答案 10 :(得分:0)
Francisco Hayoz给出的答案是最好的。如果sql可以为你做的话,不要使用pl / sql函数。
以下是Oracle 11.2.03中的简单测试
select s
, regexp_replace(s,'[^'||chr(1)||'-'||chr(127)||']','') "rep ^1-127"
, dump(regexp_replace(s,'['||chr(127)||'-'||chr(225)||']','')) "rep 127-255"
from (
select listagg(c, '') within group (order by c) s
from (select 127+level l,chr(127+level) c from dual connect by level < 129))
&#34; rep 127-255&#34;是
Typ = 1 Len = 30:226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
即由于某种原因,此版本的Oracle不会替换char(226)及更高版本。 使用&#39; [&#39; || chr(127)||&#39; - &#39; || chr(225)||&#39;]&#39;给出了期望的结果。 如果您需要替换其他字符,只需将它们添加到上面的正则表达式中,或者如果替换不同,则使用嵌套的替换| regexp_replace,然后使用&#39;&#39; (空字符串)。
答案 11 :(得分:0)
谢谢,这符合我的目的。顺便说一句,上面的示例中缺少单引号。
REGEXP_REPLACE(COLUMN,&#39; [^&#39; || CHR(32)||&#39; - &#39; || CHR(127)||&#39;]&#39 ;,&#39;&#39;))
我在自动换行功能中使用它。偶尔在传入的文本中有一个嵌入的NewLine / NL / CHR(10)/ 0A,这些都搞砸了。
答案 12 :(得分:0)
请注意,无论何时使用
regexp_like(column, '[A-Z]')
Oracle的regexp引擎也将匹配Latin-1范围内的某些字符:这适用于所有与ASCII字符类似的字符,如Ä-&gt; A,Ö-&gt; O,Ü-&gt; U等。,所以[AZ]不是你所知道的其他环境,比如Perl。
尝试在字符集升级之前更改NVARCHAR2数据类型,而不是摆弄正则表达式。
另一种方法:如果您的数据库仅包含欧洲字符(即Latin-1)字符,您可以尝试使用SOUNDEX函数,而不是删除部分字段的内容。或者您只需编写一个函数,将Latin-1范围内的字符转换为类似的ASCII字符,如
当然仅适用于转换为UTF-8时超过4000字节的文本块。
答案 13 :(得分:0)
您可以尝试以下内容来搜索包含非ascii字符的列:
select * from your_table where your_col <> asciistr(your_col);
答案 14 :(得分:0)
我也有类似的要求(为避免这种丑陋的ORA-31061: XDB error: special char to escaped char conversion failed.),但必须保持换行符。
我从出色的评论中尝试了这一点
setlocal enableDelayedExpansion
:: parse the ProgId
FOR /F "usebackq tokens=1,2,* delims==" %%A IN (`REG QUERY "HKCU\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice" /v ProgId 2^>NUL ^| more +2`) DO (
SET browserstub=%%C
:: parse for Class information
FOR /F "usebackq tokens=1,2,* delims==" %%R IN (`REG QUERY "HKCR\!browserstub!" /v AppUserModelId 2^>NUL`) DO SET "browser=%%T"
FOR /F "usebackq tokens=1,2,* delims==" %%R IN (`REG QUERY "HKCR\!browserstub!\shell" /ve 2^>NUL`) DO SET "browserverb=%%T"
FOR /F "usebackq tokens=1,2,* delims==" %%R IN (`REG QUERY "HKCR\!browserstub!\shell\!browserverb!\command" /ve 2^>NUL`) DO SET "browsercmd=%%T"
)
:: display results
SET browser
但是得到了这个ORA-12728: invalid range in regular expression。
但是它引导我找到解决方案:
'[^ -~|[:space:]]'
(在我的TOAD工具中)显示为
select t.*, regexp_replace(deta, '[^[:print:]|[:space:]]', '#') from
(select '- <- strangest thing here, and I want to keep line break after
-' deta from dual ) t
=>不在集合中的字符(打印^
或空格[:print:]
字符)答案 15 :(得分:-2)
这样做,它会起作用。
trim(replace(ntwk_slctor_key_txt, chr(0), ''))
答案 16 :(得分:-3)
我回答这个问题有点晚了,但最近遇到了同样的问题(人们把各种各样的东西剪成并粘贴成字符串而我们不知道它是什么)。 以下是一种简单的字符白名单方法:
SELECT est.clients_ref
,TRANSLATE (
est.clients_ref
, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890#$%^&*()_+-={}|[]:";<>?,./'
|| REPLACE (
TRANSLATE (
est.clients_ref
,'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890#$%^&*()_+-={}|[]:";<>?,./'
,'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
)
,'~'
)
,'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890#$%^&*()_+-={}|[]:";<>?,./'
)
clean_ref
FROM edms_staging_table est