1. 程式人生 > >oracle資料庫之base64編碼

oracle資料庫之base64編碼

  這幾天在做一個數據庫業務時,需要用到oracle資料庫的系統函式utl_encode.base64_encode()。由於之前沒用過,就從網上找了一些相關的資料以作參考,網上大部分沒有過多地介紹,基本只給出以下用法:
1.base64 的解碼函式
select utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(‘YWJjZA==’))) from dual

2.base64 的編碼函式
select utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(‘abcd’))) from dual
  上述用法沒有問題,出現問題的是utl_encode.base64_encode()這個函式。這個函式經過試驗發現,當其輸入引數的字元個數(這裡只考慮英文字元,中文字元的話應該考慮其位元組數)大於等於48時,編碼生成的字串以64個字元為一組在其後添加回車換行字元

。正是這個坑搞得我半天找不出是什麼原因導致從指定表查詢出的字串莫名其妙地被換行了,以致後續業務資料不對。大家可以參照下面的示例驗證一下:

這是我的環境:

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - 64bi
PL/SQL Release 10.2.0.1.0 - Production
CORE    10.2.0.1.0  Production
TNS for Linux: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

這是驗證方式(直接複製到sqlplus上執行):
三個星號(*)是為了直觀地顯示出有無回車換行符而加在字串末尾。

declare
    strBefore1 varchar2(100) := '123456789012345678901234567890123456789012345678';
    strAfter1 varchar2(100);
    strBefore2 varchar2(100) := '12345678901234567890123456789012345678901234567';
    strAfter2 varchar2(100);
    strBefore3 varchar2(100
) := '1234567890123456789012345678901234567890123456789'; strAfter3 varchar2(100); begin strAfter1 := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(strBefore1))); strAfter2 := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(strBefore2))); strAfter3 := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(strBefore3))); dbms_output.put_line('length of strBefore1:' || length(strBefore1)); dbms_output.put_line('length of strAfter1:' || length(strAfter1)); dbms_output.put_line('strBefore1:'); dbms_output.put_line(strBefore1 || '***'); dbms_output.put_line('strAfter1:'); dbms_output.put_line(strAfter1 || '***'); dbms_output.put_line('length of strBefore2:' || length(strBefore2)); dbms_output.put_line('length of strAfter2:' || length(strAfter2)); dbms_output.put_line('strBefore2:'); dbms_output.put_line(strBefore2 || '***'); dbms_output.put_line('strAfter2:'); dbms_output.put_line(strAfter2 || '***'); dbms_output.put_line('length of strBefore3:' || length(strBefore3)); dbms_output.put_line('length of strAfter3:' || length(strAfter3)); dbms_output.put_line('strBefore3:'); dbms_output.put_line(strBefore3 || '***'); dbms_output.put_line('strAfter3:'); dbms_output.put_line(strAfter3 || '***'); end; /

這是實驗結果:
  從結果中可以看出strAfter31和strAfter3字串中插有回車換行符,要證明是這兩個字元,各位可自行通過substr()函式(獲取子串)和ascii()函式獲取多出的兩個字元的ascii值。另外提一點,正常的base64編碼後的字串的長度是4的倍數。
  對此問題我沒有找到相關資料,oracle官方文件也沒有說明。
文件地址:http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/u_encode.htm#CACECFHF

length of strBefore1:48
length of strAfter1:66
strBefore1:
123456789012345678901234567890123456789012345678***
strAfter1:
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4
***
length of strBefore2:47
length of strAfter2:64
strBefore2:
12345678901234567890123456789012345678901234567***
strAfter2:
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc=***
length of strBefore3:49
length of strAfter3:70
strBefore3:
1234567890123456789012345678901234567890123456789***
strAfter3:
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4
OQ==***

PL/SQL procedure successfully completed.

解決辦法:
要解決這個問題其實很簡單(當時想複雜了,本想分組逐個去掉回車換行符),就是使用替換函式

1. regexp_replace(source_string, expression [, replace_string [, position [, occurrence [, match_parameter]]]])
這是一個正則表示式替換函式。
source_stirng 待搜尋的字串。
expression 一個用來描述要尋找文字的模式的正則表示式。
replace_string 查詢替換中要替換成的文字。
position 源字串source_string中搜索開始的字元位置。預設是1。
occurrence 要定位的模式出現的次數。預設是1,也就是第一次匹配。
match_parameter 這個字串指定選項,改變正則表示式匹配引擎的行為。
針對當前問題的用法:regexp_replace(strAfter, '\s', '')
'\s'會匹配所有空白字元,包括回車符、換行符、製表符、垂直製表符、跳頁符、空格等。

2. replace(string1, match_string, replace_string)
這是一個普通的替換函式。把字串string1中的match_string替換為replace_string,並返回替換後的字串。這一函式同時實現了查詢替換功能。
針對當前問題的用法:replace(strAfter, chr(10) || chr(13), '')
chr(10)表示換行符;chr(13)表示回車符。

3. replace(string1, match_string)
這是一個普通的替換函式。把string1中的所有match_string都刪除掉並返回刪除後的字串。
針對當前問題的用法:replace(strAfter, chr(10) || chr(13))

注:這三個函式的解釋摘抄自《oracle PL/SQL程式設計 第五版》上冊216頁,下冊1097頁。

這裡只用了正則表示式替換函式,好處是所有空白都能替換為空字元,而不管是否是回車換行字元。

declare
    strBefore1 varchar2(100) := '123456789012345678901234567890123456789012345678';
    strAfter1 varchar2(100);
    strBefore2 varchar2(100) := '12345678901234567890123456789012345678901234567';
    strAfter2 varchar2(100);
    strBefore3 varchar2(100) := '1234567890123456789012345678901234567890123456789';
    strAfter3 varchar2(100);
begin
    strAfter1 := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(strBefore1)));
    strAfter2 := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(strBefore2)));
    strAfter3 := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(strBefore3)));
    strAfter1 := regexp_replace(strAfter1, '\s', '');
    strAfter2 := regexp_replace(strAfter2, '\s', '');
    strAfter3 := regexp_replace(strAfter3, '\s', '');
    dbms_output.put_line('length of strBefore1:' || length(strBefore1));
    dbms_output.put_line('length of strAfter1:' || length(strAfter1));
    dbms_output.put_line('strBefore1:');
    dbms_output.put_line(strBefore1 || '***');
    dbms_output.put_line('strAfter1:');
    dbms_output.put_line(strAfter1 || '***');

    dbms_output.put_line('length of strBefore2:' || length(strBefore2));
    dbms_output.put_line('length of strAfter2:' || length(strAfter2));
    dbms_output.put_line('strBefore2:');
    dbms_output.put_line(strBefore2 || '***');
    dbms_output.put_line('strAfter2:');
    dbms_output.put_line(strAfter2 || '***');

    dbms_output.put_line('length of strBefore3:' || length(strBefore3));
    dbms_output.put_line('length of strAfter3:' || length(strAfter3));
    dbms_output.put_line('strBefore3:');
    dbms_output.put_line(strBefore3 || '***');
    dbms_output.put_line('strAfter3:');
    dbms_output.put_line(strAfter3 || '***');
end;
/

這是上述解決辦法的實驗結果:

length of strBefore1:48
length of strAfter1:64
strBefore1:
123456789012345678901234567890123456789012345678***
strAfter1:
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4***
length of strBefore2:47
length of strAfter2:64
strBefore2:
12345678901234567890123456789012345678901234567***
strAfter2:
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc=***
length of strBefore3:49
length of strAfter3:68
strBefore3:
1234567890123456789012345678901234567890123456789***
strAfter3:
MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OQ==***

PL/SQL procedure successfully completed.

  上述說明如有問題,歡迎批評指正。