我的问题涉及这种情况。我在 Tcl 接口中使用 SQLite 的增量 blob I/O 将文本存储为 BLOB。数据库中的另一个表包含指向此 BLOB 段的指针数据行。由于 Tcl 通道寻求字节位置而不是字符位置(我认为这是常见的,我不是批评 Tcl),因此我需要跟踪每个指针的 byte_start、char_start、byte_length、char_length。
Tcl 收到一个请求,指示现有指针(不是实际的缓冲区数据,而只是指针本身)需要拆分为两个指针,从某个字符位置开始。该请求来自 UI,该代码对字节位置一无所知,对 BLOB 也一无所知。
因此,我需要提取 BLOB 的段,将其转换为文本,并确定两个新片段中至少一个的字节长度,并使用该信息来确定两个新指针的起始字节和字节长度。
我打算在 SQLite 中使用如下所示的内容,但是根据 Hipp 博士的说法,必须将整个 BLOB 读入内存才能在 BLOB 上使用 substr。
select
octet_length(
substr(
cast(
substr(
buffer,
byte_start,
byte_length
) as text
),
1,
:len
)
)
from
mem.pt_buffers
where
doc_id = :doc_id
and buffer_id = :buffer_id_s
;
因此,我在 Tcl 中使用 incrblob,如本示例所示。它似乎生成了正确的结果,但似乎也需要做很多工作。例如,从 BLOB 中提取内容只是为了确定分割点的字符位置的字节位置。内容不会以其他方式使用,BLOB 也不会被修改。如果不存在 UI 未知字节位置的问题,则不需要提取内容,并且操作将只是算术运算。
我的问题是:
感谢您考虑我的问题。
package require sqlite3
sqlite3 db
db eval {create table test (id integer, data blob);}
db eval {insert into test values (1, zeroblob(100));}
puts [db eval {select id, cast(data as text) from test;}]
# 1 {}
# Previously, a request had to come in to append this string
# to the BLOB; to the non-zero portion, anyway. This is re-
# quired set-up for the question.
set fdBlob [db incrblob main test data 1]
chan configure $fdBlob -translation binary -buffering none
set bindata [encoding convertto utf-8\
{This is some הַ / נָּבִ֑יא text cast as a BLOB.}]
chan puts -nonewline $fdBlob $bindata
puts [db eval {
select
length(data),
length(cast(data as text)),
length(:bindata)
from
test
;
}]
# 100 47 57
# Pre-split pointer data:
# piece char_start char_length byte_start byte_length
# ----- ---------- ----------- ---------- -----------
# orig 0 47 0 57
# Request comes in to split the pointer at the 27th character
# into two pointers: characters 0-26 and 27-end.
# Retrieve the segment of the BLOB. Have to retrieve the full
# piece because do not know where to split the bytes. Convert
# from binary to text. Note [chan read numChars] reads chars,
# but since channel is configured as binary, same as bytes.
chan seek $fdBlob 0 start
set data [encoding convertfrom utf-8 [chan read $fdBlob 57]]
# Split the piece. Need only one or the other, not both.
set frontEnd [string range $data 0 26]
set tailEnd [string range $data 27 end]
puts "\"$frontEnd\" \"$tailEnd\""
# "This is some הַ / נָּבִ֑יא " "text cast as a BLOB."
# Convert the substring back to binary and determine byte length.
set frontEndByteLen [string length [encoding convertto utf-8 $frontEnd]]
set tailEndByteLen [expr {57-$frontEndByteLen}]
puts "Front-end data: byte_start: 0 byte_length $frontEndByteLen"
puts "Tail-end data: byte_start: $frontEndByteLen byte_length $tailEndByteLen"
# Front-end data: byte_start: 0 byte_length 37
# Tail-end data: byte_start: 37 byte_length 20
# Test it out by seeking to the start byte and extracting the tail-end.
chan seek $fdBlob $frontEndByteLen start
set data [encoding convertfrom utf-8 [chan read $fdBlob $tailEndByteLen]]
puts $data
# text cast as a BLOB.
# Then the pointer table will have these new pieces inserted.
# piece char_start char_length byte_start byte_length
# ----- ---------- ----------- ---------- -----------
# front 0 27 0 37
# tail 27 20 37 20
你的例子是我将如何处理它,特别是如果字符偏移来自 Tcl UI。由于 sqlite 是进程内的,因此无需考虑网络延迟 - 这实际上只是字符偏移计算是由 sqlite 的代码还是 Tcl 的代码执行的。 Tcl 在这方面真的很擅长(真的很喜欢字符串和一切)。
特别是如果 UI 是 Tcl,因为字符索引在代理对等情况下会一致。
仅选择 blob 值,使用
string range
命令拆分并更新行可能会更有效,但这将取决于 db 值的通道包装如何实现以及它意味着什么复杂性的具体情况。最好对您的实际案例进行基准测试才能知道。另一个考虑因素是,虽然 chan seek
对字节偏移量进行操作,但 chan read
对字符进行操作,因此以这种方式获取片段可能会更容易(特别是如果它是您想要的第一部分 - 只需寻求 0 并读取那么多字符)