将字符串分割成等长段的有效方法?

问题描述 投票:0回答:1

有没有一种快速方法可以在 SQLite 和/或 Tcl 中将字符串分割成相等长度的段?

我的目标是在内存中获取一个字符串并将分割写入表中的行。有时字符串通过通道传递,有时它是由 SQLite 表中已保存的片段连接而成。

我一直在使用具有

substr
的 CTE,对于将约 1 MB 的字符串分解为 4216 条/行,我能得到的最好时间约为 1.6 秒。

最简单的拆分尝试示例是

select
   sg.value, substr(:textContent, sg.value, 250)
from
   generate_series(1, :tcLen, 250) sg
;

使用

group_concat
组合表中的 4216 个片段仅需 0.129 秒,其中每行上都有一个
substr
来选择每行字符串的全部或部分。

感谢您提供的任何指导。

编辑:

我现在看到这个answer

:textutil::spit::splitn
中提到了
tcllib
。会尝试一下;应该考虑到那里查看,但假设 SQL 会更快。

使用如下文本实用程序并在循环中插入,将 1.6 秒减少到约 0.26 秒。

foreach row [::textutil::split::splitn $textContent $bufferRowMaxLength] {
  set charLength [expr {min([string length $row], $bufferRowMaxLength)}]
  db eval {
    insert into mem.pt_buffers values ( ... );
    insert into mem.pt_pointer values ( ... );
  }
}
sqlite tcl
1个回答
0
投票

textutil::split::splitn
非常快,尽管它只是该解决方案的明显 Tcl 脚本,比我用 c 所能做到的最佳速度慢大约 2 倍。这其实不应该太令人惊讶,Tcl 确实很擅长处理字符串。

我尝试过的一个替代方案(基于 Donal 多年前分享的一个技巧),是用

regexp
:

将其分块
set chunks [regexp -all -inline .{1,$bufferMaxRowLength} $textContent]

但这出奇的慢。

这是我的所有结果:

package require textutil
package require jitc 0.5.3

set str [string repeat a 1048576]

set bufferRowMaxLength  250

set split_cdef {
    code {
        #include <string.h>

        OBJCMD(split) {
            int         code = TCL_OK;
            Tcl_Obj**   chunkobjs = NULL;
            Tcl_Obj*    res = NULL;

            enum {A_cmd, A_STR, A_CHUNK, A_objc};
            CHECK_ARGS_LABEL(finally, code, "str chunk");
            int len;
            const char* str = Tcl_GetStringFromObj(objv[A_STR], &len);
            int chunk;
            TEST_OK_LABEL(finally, code, Tcl_GetIntFromObj(interp, objv[A_CHUNK], &chunk));
            const int chunks = (len + chunk - 1) / chunk;
            chunkobjs = ckalloc(chunks * sizeof(chunkobjs[0]));
            for (int i=0; i<chunks; i++) {
                const int start = i * chunk;
                const int end = start + chunk;
                const int clen = end < len ? chunk : len - start;
                chunkobjs[i] = Tcl_NewStringObj(str + start, clen);
            }
            replace_tclobj(&res, Tcl_NewListObj(chunks, chunkobjs));
            Tcl_SetObjResult(interp, res);

        finally:
            replace_tclobj(&res, NULL);
            if (chunkobjs) ckfree(chunkobjs);
            return code;
        }

        OBJCMD(foreach_chunk) {
            int         code = TCL_OK;

            enum {A_cmd, A_STR, A_CHUNK, A_VAR, A_SCRIPT, A_objc};
            CHECK_ARGS_LABEL(finally, code, "str chunk var script");
            int len;
            const char* str = Tcl_GetStringFromObj(objv[A_STR], &len);
            Tcl_WideInt chunk;
            TEST_OK_LABEL(finally, code, Tcl_GetWideIntFromObj(interp, objv[A_CHUNK], &chunk));
            const int chunks = (len + chunk - 1) / chunk;
            for (int i=0; i<chunks; i++) {
                const int start = i * chunk;
                const int end = start + chunk;
                const int clen = end < len ? chunk : len - start;
                if (NULL == Tcl_ObjSetVar2(interp, objv[A_VAR], NULL, Tcl_NewStringObj(str + start, clen), TCL_LEAVE_ERR_MSG)) {
                    code = TCL_ERROR;
                    goto finally;
                }
                TEST_OK_LABEL(finally, code, Tcl_EvalObjEx(interp, objv[A_SCRIPT], 0));
            }

        finally:
            return code;
        }
    }
}

jitc::bind jitc_split         $split_cdef split
jitc::bind jitc_foreach_chunk $split_cdef foreach_chunk

set chunk   .{1,$bufferRowMaxLength}

puts "regexp: [timerate {
    set pieces [llength [regexp -all -inline $chunk $str]]
}], pieces: $pieces"

puts "splitn: [timerate {
    set pieces  [llength [::textutil::split::splitn $str $bufferRowMaxLength]]
}], pieces: $pieces"

puts "jitc split: [timerate {
    set pieces [llength [jitc_split $str $bufferRowMaxLength]]
}], pieces: $pieces"

puts "jitc foreach_chunk: [timerate {
    set pieces  0
    jitc_foreach_chunk $str $bufferRowMaxLength chunk {
        incr pieces
    }
}], pieces: $pieces"
regexp: 348330.7 µs/# 3 # 2.871 #/sec 1044.992 net-ms, pieces: 4195
splitn: 567.176 µs/# 1764 # 1763.1 #/sec 1000.498 net-ms, pieces: 4195
jitc split: 235.065 µs/# 4255 # 4254.1 #/sec 1000.201 net-ms, pieces: 4195
jitc foreach_chunk: 463.043 µs/# 2160 # 2159.6 #/sec 1000.173 net-ms, pieces: 4195

regexp
的实施比其他实施慢得多,这表明其实施还有一些改进的空间,但存在一些不明显的低效率。

但对我来说,要点是,只要您具有将 Tcl 列表与块内容组装在一起的约束,那么

::textutil::split::splitn
(或等效的几行 Tcl)可能是正确的方法。在我的书中,C 实现增加的复杂性并不足以证明其合理性,而且几乎肯定会被对块本身所做的任何工作完全抵消。

© www.soinside.com 2019 - 2024. All rights reserved.