2013-07-02 58 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'来定位和覆盖'来代替循环。 –