2013-07-02 92 views
1

我想對二進制字符串執行子字符串替換操作。有可用的功能,這是否確切的事情(c.f.text類型的字符串:用二進制字符串替換子字符串

replace(string text, from text, to text) 

但遺憾的是沒有爲bytea型(c.f.)的二進制字符串。

現在我想知道,是否需要爲二進制字符串重新實現此操作,還是可以使用相應的基本字符串函數執行此任務?是否有邊緣的情況下,可以打破我的應用程序:

select replace('\000\015Hello World\000\015Hello World'::bytea::text, 
       'World', 
       'Jenny')::bytea 

的文檔中我找不到一個具體的音符爲止。有人可以幫助我嗎?

+0

什麼是你的PostgreSQL的版本?您提供的鏈接混合使用9.2和8.0文檔頁面,並且字符串中反斜槓的解釋也取決於版本。 –

+0

@DanielVérité它的版本是9.2 – moooeeeep

回答

1

取代據@DanielVérité的建議,我已經實現了一個plpgsql功能,做一個字符串bytea類型的二進制字符串替換。 在實現中,我只使用二進制字符串部分的函數,所以我認爲它應該是安全的使用。

這裏是我的代碼:

CREATE OR REPLACE FUNCTION 
replace_binary(input_str bytea, pattern bytea, replacement bytea) 
RETURNS bytea 
AS $$ 
DECLARE 
    buf bytea; 
    pos integer; 
BEGIN 
    buf := ''; 
    -- validate input 
    IF coalesce(length(input_str), 0) = 0 OR coalesce(length(pattern), 0) = 0 
    THEN 
     RETURN input_str; 
    END IF; 
    replacement := coalesce(replacement, ''); 
    LOOP 
     -- find position of pattern in input 
     pos := position(pattern in input_str); 
     IF pos = 0 THEN 
      -- not found: append remaining input to buffer and return 
      buf := buf || substring(input_str from 1); 
      RETURN buf; 
     ELSE 
      -- found: append substring before pattern to buffer 
      buf := buf || substring(input_str from 1 for pos - 1); 
      -- append replacement 
      buf := buf || replacement; 
      -- go on with substring of input 
      input_str := substring(input_str from pos + length(pattern)); 
     END IF; 
    END LOOP; 
END; 
$$ LANGUAGE plpgsql 
IMMUTABLE; 

至於我的測試情況下,它工作得很好:

with input(buf, pattern, replacement) as (values 
    ('tt'::bytea, 't'::bytea, 'ttt'::bytea), 
    ('test'::bytea, 't'::bytea, 'ttt'::bytea), 
    ('abcdefg'::bytea, 't'::bytea, 'ttt'::bytea), 
    ('\000\015Hello 0orld\000\015Hello 0orld'::bytea, '0'::bytea, '1'::bytea)) 

select encode(replace_binary(buf, pattern, replacement), 'escape') from input; 

產出預期:

   encode    
------------------------------------ 
tttttt 
tttesttt 
abcdefg 
\000\rHello 1orld\000\rHello 1orld 
(4 rows) 
1

與鑄件text,回到bytea的問題是,如果替換字符串引用涉及字節的字符串這是行不通的。我們來看一個例子。

(我設置bytea_outputhex更清楚地看到文字,否則這一切都十六進制數)

初始查詢:

with input(x) as (values (('\000\015Hello World\000\015Hello World'::bytea))) 
    select replace(x::text, 'World', 'Jenny')::bytea from input; 

結果是罰款:

 
       replace     
---------------------------------------- 
\000\015Hello Jenny\000\015Hello Jenny 
(1 row) 

但如果試圖用一個修改後的版本替換字符01

with input(x) as (values (('\000\015Hello 0orld\000\015Hello 0orld'::bytea))) 
    select replace(x::text, '0', '1')::bytea from input; 

結果是:

 
       replace     
---------------------------------------- 
IMHello 1orldIMHello 1orld 

而所期望的結果將是:\000\015Hello 1orld\000\015Hello 1orld。 這是因爲中間表示\000\015得到由\111\115

+0

我不知道。我認爲':: text'表示會將不可打印字節保留爲單個字節,但實際上'octet_length(x)'不等於'octet_length(x :: text)'。是否存在/您是否知道解決方法? – moooeeeep

+0

我沒有看到一個簡單的解決方法。我想我會在'plpgsql'中迭代執行,而不用強制轉換爲文本,並使用'position'來定位和覆蓋'來代替循環。 –