【发布时间】:2016-11-29 01:00:03
【问题描述】:
我有一个大小约为 3.5mb 的 xml 文件。我正在对其进行编码并将其传播到另一个需要对其进行解码的数据库。我在迭代中对其进行解码,因为它太大而无法一次解码,但是在某些迭代中它无法解码,我得到了一些乱码。我相信这是因为一些符号可以是 1 个字节,而另一些可以是 2 个字节,有时子字符串将一个 2 个字节的符号切成两半,并且迭代会出现乱码。我想我可以尝试将每个子字符串转换为 clob,因为它在转换失败时会发出警告,如果出现警告,则将子字符串中的数量增加一些数字,但我还没有成功解码这个方法。有什么解决方法吗?
更新
解码成功,有警告检查。您需要做的就是尝试使用dbms_lob.convertblobtoclob 将子字符串转换为clob,检查warning != 0,如果是这样,将偏移量减少1并进行下一次迭代,而不将子字符串写入您的blob。然而,这是非常耗费内存的检查,因为它需要创建 blob 并将该 blob 转换为 clob。有没有更简单的解决方法,也许我遗漏了一些非常明显的东西?
更新
我有一个 xml 文件,其中包含以 base64 编码的付款 xml,以及有关该 xml 的其他数据。 f.e.
<envelope>
<file_name>a.xml</file_name>
<...><...>
<data>BASE64 ENCODED XML FILE</data>
</envelope>
包含几个示例的完整脚本。之前我提到过警告检查的解决方法,但在这个例子中它似乎不起作用,当我更好地考虑它时,它不应该。无论如何,这是脚本:
declare
l_clob clob := empty_clob();
function convert_clob_to_blob(
p_clob in clob) return blob is
l_dest_offsset number := 1;
l_src_offsset number := 1;
l_lang_context number := dbms_lob.default_lang_ctx;
l_warning number;
l_result blob;
begin
dbms_lob.createtemporary(l_result, false);
dbms_lob.convertToBlob(
dest_lob => l_result,
src_clob => p_clob,
amount => dbms_lob.lobmaxsize,
dest_offset => l_dest_offsset,
src_offset => l_src_offsset,
blob_csid => dbms_lob.default_csid,
lang_context => l_lang_context,
warning => l_warning);
if l_warning != 0 then
raise_application_error(-20001, 'sd' || '.convert_blob_to_clob ' || l_warning);
end if;
return l_result;
end;
function gen_rand_xml return clob is
l_xml xmltype := xmltype('<envelope><nullnode></nullnode></envelope>');
begin
for i in 1..50 loop
SELECT
insertXMLafter(
l_xml,
'/envelope/nullnode',
XMLType('<node>' || i || '</node>'))
INTO
l_xml
FROM dual;
end loop;
return l_xml.getClobVal();
end;
function to_base64(
p_clob in clob) return clob is
l_length integer;
l_offset integer := 1;
l_amt binary_integer := 600;
l_buffer varchar2(1800);
l_result clob := empty_clob();
l_temp_blob blob;
begin
dbms_lob.createtemporary(l_temp_blob, false);
l_temp_blob := convert_clob_to_blob(p_clob);
l_length := dbms_lob.getlength(l_temp_blob);
while l_offset < l_length loop
l_result := l_result || utl_raw.cast_to_varchar2(utl_encode.base64_encode(dbms_lob.substr(l_temp_blob, l_amt, l_offset)));
l_offset := l_offset + l_amt;
end loop;
return l_result;
end;
function from_base64(
p_clob in clob) return clob is
l_length integer := dbms_lob.getLength(p_clob);
l_offset integer := 1;
l_amt binary_integer := 800;
l_buffer varchar2(3200);
l_result clob := empty_clob();
begin
while l_offset <= l_length loop
l_buffer := replace(replace(dbms_lob.substr(p_clob, l_amt, l_offset), chr(10), null), chr(13), null);
l_offset := l_offset + l_amt;
while l_offset <= l_length and mod(dbms_lob.getLength(l_buffer), 4) > 0 loop
l_buffer := l_buffer || replace(replace(dbms_lob.substr(p_clob, 1, l_offset), chr(10), null), chr(13), null);
l_offset := l_offset + 1;
end loop;
l_result := l_result || utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(l_buffer)));
end loop;
return l_result;
end;
procedure print_clob( p_clob in clob )
as
l_offset number default 1;
begin
loop
exit when l_offset > dbms_lob.getlength(p_clob);
dbms_output.put_line( dbms_lob.substr( p_clob, 4000, l_offset ) );
l_offset := l_offset + 4000;
end loop;
end;
begin
l_clob := gen_rand_xml;
print_clob(from_base64(to_base64(l_clob)));
end;
/
【问题讨论】:
-
请向我们展示您的代码。为什么要尝试将 BLOB 转换为 CLOB?对于 XML 文件,在
CLOB中工作应该没问题(除非 XML 是 UTF-8,但您的数据库在“较低”字符集上工作,例如WE8ISO8859P1- 但你仍然可以使用NCLOB) -
当你使用
base64_encode时,那么数据量必须是3的整数倍,即l_amt binary_integer := 1000;不起作用,比如使用l_amt binary_integer := 999;。 -
这比我想象的要难。实际上你必须使用
CONVERTTOBLOB因为 BASE64 需要 3 个 Bytes (不是字符)并将其转换为 4 Bytes。另一个挑战是您在 BASE64 字符串中可能包含的 NEW_LINE 字符,解码时必须忽略这些字符。请查看更新的答案,对我来说,它甚至可以使用XMLTYPE('<ñödê>' || i || '</ñödê>')之类的字符串 -
如果您的输入字符串包含特殊字符(即像
ñödê这样的非ASCII),则在解码时必须转换为BLOB。否则我会回来一团糟。 -
很可能只发生在 UTF-8 / UTF-16 字符集上。使缓冲区更小(例如
l_amt binary_integer := 36;),然后您应该会看到它。