使用 incrblob 根据文本字符串中的字符位置确定 BLOB 段中的字节位置?

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

我的问题涉及这种情况。我在 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 未知字节位置的问题,则不需要提取内容,并且操作将只是算术运算。

我的问题是:

  1. 我这样做是否很艰难?有没有更简单的方法?
  2. 更多的事情可以/应该直接在 SQLite 中完成吗?

感谢您考虑我的问题。

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

sqlite tcl
1个回答
0
投票

你的例子是我将如何处理它,特别是如果字符偏移来自 Tcl UI。由于 sqlite 是进程内的,因此无需考虑网络延迟 - 这实际上只是字符偏移计算是由 sqlite 的代码还是 Tcl 的代码执行的。 Tcl 在这方面真的很擅长(真的很喜欢字符串和一切)。

特别是如果 UI 是 Tcl,因为字符索引在代理对等情况下会一致。

仅选择 blob 值,使用

string range
命令拆分并更新行可能会更有效,但这将取决于 db 值的通道包装如何实现以及它意味着什么复杂性的具体情况。最好对您的实际案例进行基准测试才能知道。另一个考虑因素是,虽然
chan seek
对字节偏移量进行操作,但
chan read
对字符进行操作,因此以这种方式获取片段可能会更容易(特别是如果它是您想要的第一部分 - 只需寻求 0 并读取那么多字符)

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