当前位置: 首页 > 知识库问答 >
问题:

PostgreSQL:regex用大括号替换第一级方括号

咸亦
2023-03-14

我有一个类型TEXT的PostgreSQL列中的数据,我需要做一些字符替换。具体来说,我想用大括号代替方括号。问题是,我只想替换不超过两层深的括号,如果你包括主要的封闭括号。这些字符串可能很长,所以我认为正则表达式可能是最好的方法regexp_replace函数),但我不擅长正则表达式。这里有一个这样的值的例子:

[0,0,0,[12,2],0,0,[12,[1,2,3]],12,0,[12,2,[2]],12,0,12,0,0]

因此,我希望此字符串更改为:

{0,0,0,{12,2},0,0,{12,[1,2,3]},12,0,{12,2,[2]},12,0,12,0,0}

提前谢谢!

共有3个答案

秦炜
2023-03-14

只是为了好玩,这里有一个完全SQL的解决方案。它使用CTE以实现符号的清晰,但是您可以在INF中使用子查询,因为没有递归CTE的使用。

编辑:添加简化,更快SQL版本,版本在Pl/Python,版本在C.C型稍微快一点——大约快250倍。

create or replace function repl(text) 
returns text 
language sql
as $$
with 
chars(pos, ch) as (
    -- In PostgreSQL 9.4 this can be replaced with an UNNEST ... WITH ORDINALITY
    -- it turns the string into a list of chars accompanied by their position within
    -- the string.
    select row_number() OVER (), ch
    from regexp_split_to_table($1,'') ch
),
nesting(ch, pos, lvl) as (
    -- This query then determines how many levels of nesting of [s and ]s are
    -- in effect for each character.
    select ch, pos, 
        sum(case ch when '[' then 1 when ']' then -1 else 0 end) OVER (ORDER BY pos) 
        from chars
),
transformed(ch, pos) as (
    -- and this query transforms [s to {s or ]s to }s if the nesting
    -- level is appropriate. Note that we use one less level of nesting
    -- for closing brackets because the closing bracket it self has already
    -- reduced the nesting level.
    select 
      case
        when ch = '[' and lvl <= 2 then '{' 
        when ch = ']' and lvl <= 1 then '}' 
        else ch
      end,
      pos
    from nesting
)
-- Finally, reconstruct the new string from the (char, position) tuples
select 
  string_agg(ch, '' order by pos)
from transformed;
$$;

但是,它比其他解决方案慢。

  • Johnny 5的regexp解决方案需要450毫秒才能完成10000次迭代
  • wildplasser的replbracket需要950ms才能完成10000次迭代
  • 此CTE解决方案需要2050毫秒才能完成10000次迭代

摆脱CTE并使用unnest。。。使用“有序性”可将其速度提高到约1400ms:

create or replace function repl(text) returns text language sql volatile as
$$
    select
      string_agg(ch, '' order by pos)
    from (
        select
          case
            when ch = '[' and sum(case ch when '[' then 1 when ']' then -1 else 0 end) OVER (ORDER BY pos) <= 2 then '{'
            when ch = ']' and sum(case ch when '[' then 1 when ']' then -1 else 0 end) OVER (ORDER BY pos) <= 1 then '}'
            else ch
          end,
          pos
        from unnest(regexp_split_to_array($1,'')) with ordinality as chars(ch, pos)
    ) as transformed(ch, pos)
$$;

如果您想要快速,请使用适当的过程语言-或PL/Python2中的C:

create or replace function replpy(instr text) returns text language plpythonu as $$
def pyrepl(instr):
    level=0
    for ch in instr:
        if ch == '[':
                level += 1
                if level <= 2:
                        yield '{'
                else:
                        yield '['
        elif ch == ']':
                if level <= 2:
                        yield '}'
                else:
                        yield ']'
                level -= 1
        else:
                yield ch

return ''.join(pyrepl(instr))
$$;

它需要160毫秒。

好吧,鞭打一匹死马,让我们用c来做。作为扩展的完整源代码在这里,但这里是. c文件:

#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(replc);
Datum replc(PG_FUNCTION_ARGS);

PGDLLEXPORT Datum
replc(PG_FUNCTION_ARGS)
{
    /* Set `buf` to a palloc'd copy of the input string, deTOASTed if needed */
    char * const buf = text_to_cstring(PG_GETARG_TEXT_PP(0));
    char * ch = buf;
    int depth = 0;


    while (*ch != '\0')
    {
        switch (*ch)
        {
            case '[':
                depth++;
                if (depth <= 2)
                    *ch = '{';
                break;
            case ']':
                if (depth <= 2)
                    *ch = '}';
                depth--;
                break;
        }
        ch++;
    }
    if (depth != 0)
        ereport(WARNING,
                (errmsg("Opening and closing []s did not match, got %d extra [s", depth)));

    PG_RETURN_DATUM(CStringGetTextDatum(buf));
}

运行时间:8毫秒,用于10000次迭代。很好,它比原来快了250倍,这是强制子查询的开销造成的。

杜联
2023-03-14

这是丑陋的,但它工作(并避免regexp复杂性;-)我希望我有所有的角落案例涵盖...

CREATE OR REPLACE FUNCTION replbracket( _source text ) returns text
AS $func$
DECLARE
        pos_end INTEGER;
        pos_begin INTEGER;
        level INTEGER;
        result text;
BEGIN
        result = '' ;
        level = 0;
LOOP
        pos_begin = position ( '[' IN _source );
        pos_end = position ( ']' IN _source );
        -- raise notice 'Source=% Result=% Begin = % End=%'
                -- ,_source, result, pos_begin, pos_end;

        if (pos_begin < 1 AND pos_end < 1) THEN EXIT ;
        elsif (pos_begin < 1 ) THEN pos_begin =  pos_end + 1 ;
        elsif (pos_end < 1 ) THEN pos_end =  pos_begin + 1 ;
        end if;
        if (pos_begin < pos_end) THEN
                result = result || LEFT(_source, pos_begin-1);
                level = level + 1;
                if (level <= 2) THEN result = result || '{'; else result = result || '['; end if;
                _source = SUBSTR(_source, pos_begin+1);
        ELSE
                result = result || LEFT(_source, pos_end-1);
                level  = level - 1;
                if (level < 2) THEN result = result || '}'; else result = result || ']'; end if;
                _source = SUBSTR(_source, pos_end+1);
        END IF;
END LOOP;
        result = result || _source ;
        return result;
END

$func$ LANGUAGE plpgsql;
唐哲
2023-03-14

这将是一个痛苦的正则表达式,因为在PostgreSQL风格中可能没有递归可用。

对于最多2级嵌套深度检查,如果以下双替换工作(不能测试它)

regexp_replace(
  regexp_replace('str', E'\\[(([^][]|\\[([^][]|\\[[^][]*\\])*\\])*)\\]', E'{\\1}', 'g')
, E'\\[(([^][]|\\[([^][]|\\[[^][]*\\])*\\])*)\\]', E'{\\1}', 'g')

其想法是在两次过程中匹配并替换最外层的[]。请参见regex101中的示例:

传递1:{0,0,0,[12,2],0,0,[12,[1,2,3],12,0,[12,2,[2]],12,0,12,0,0}
传递2:{0,0,0,{12,2},0,0,{12,[1,2,3]},12,0,{12,2,[2]},12,0,12,0,0}

\[[^][]*\]匹配一个实例的[...]

  • \[/code>开口方括号

注意,如果字符串总是以[开始,以]结束,并且表示0级的一个实例(不被][分隔),第一个/内部regexp_replace也可以通过替换[at^start和]at$end:E'^\\[(.*)\\]$'withE'{\\1}'

要在这里添加嵌套,请使用最多4个深度级别的示例:

\[([^][]|    # outer
\[([^][]|    # lvl 1
\[([^][]|    # lvl 2
\[([^][]|    # lvl 3
\[[^][]*\]   # lvl 4
)*\]
)*\]
)*\]
)*\]

将外部[]中的内容包装到一个捕获组中,4个级别的模式将变为:

\[(([^][]|\[([^][]|\[([^][]|\[([^][]|\[[^][]*\])*\])*\])*\])*)\]

regex\u replace一起使用时可能需要额外转义[]

\\[(([^][]|\\[([^][]|\\[([^][]|\\[([^][]|\\[[^][]*\\])*\\])*\\])*\\])*)\\]

这可以像两个过程中的第一个模式一样使用,并替换为E'{\\1}'

 类似资料:
  • Note: 这个特性从 FreeMarker 2.3.4 版本后才存在。 FreeMarker支持一个替换的语法。就是在FreeMarker的指令和注释中用 [ 和 ] 来代替 < 和 >,例如下面这个例子: 调用预定义指令:[#list animals as animal]...[/#list] 调用自定义指令:[@myMacro /] 注释:[#-- the comment --] 为了使用这

  • 我试图创建一个数据数组与FLOT图表一起使用。我使用这个CodeIgniter代码来生成数组: 这段代码的问题是无法绘制生成的JSON对象,因为“值”实际上是字符串。以下是有问题的JSON输出: 这个JSON有三个问题: 需要方括号而不是花括号;和 以下是JSON输出的格式: 清理这个JSON对象的最佳方法是什么?我应该在服务器端使用CodeIgniter代码吗?或者我应该在javascript中

  • 以下是降价文本的示例: #“我的标题” !图像标题。{ 样式=“浮动:右; 宽度: 20%; 边框: 1px”} 有的“引用文字”,有的*“强调文字”*等。 在bash脚本中,我试图用法语引号替换任何双引号。 例如:“word”应变成« 换句话说,一个单词前的所有引号都应该替换为一个开放的法语引号,后跟一个不间断的空格;而且一个单词后面的所有引号都要换成一个不换行的空格后面跟着一个闭合的法语引号;

  • 本文向大家介绍使用JS正则表达式 替换括号,尖括号等,包括了使用JS正则表达式 替换括号,尖括号等的使用技巧和注意事项,需要的朋友参考一下 如下所示: 以上这篇使用JS正则表达式 替换括号,尖括号等就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持呐喊教程。

  • 在初始化(例如新列表)时使用括号中的括号实际上意味着什么? 这是否意味着创建新的引用方法后正在调用?

  • 本文向大家介绍浅析python 中大括号中括号小括号的区分,包括了浅析python 中大括号中括号小括号的区分的使用技巧和注意事项,需要的朋友参考一下 python语言最常见的括号有三种,分别是:小括号( )、中括号[ ]和大括号也叫做花括号{ }。其作用也各不相同,分别用来代表不同的python基本内置数据类型。 1.python中的小括号( ):代表tuple元组数据类型,元组是一种不可变序列