测试DuckDB电子表格读取插件rusty_sheet 0.2版
又在超第一时间拿到了张泽鹏先生的rusty_sheet插件0.2版,迫不及待装上测试,却发现DuckDB对插件版本卡得很严,1.4.0都用不了1.4.1版的插件。这难不倒我,一边下载新版本DuckDB,一边让deepseek编了个python程序,把插件中间的1.4.1都替换成了1.4.0,就可以在1.4.0中用了。
代码如下:
import sys
import osdef replace_string_in_binary(filename, old_str, new_str):"""在二进制文件中替换指定字符串"""# 检查文件是否存在if not os.path.exists(filename):print(f"错误: 文件 '{filename}' 不存在")return False# 检查新旧字符串长度是否相同if len(old_str) != len(new_str):print(f"错误: 新旧字符串长度必须相同 ('{old_str}' -> '{new_str}')")return Falsetry:# 读取二进制文件with open(filename, 'rb') as f:content = f.read()# 将二进制内容转换为字节数组以便修改content_bytearray = bytearray(content)# 将字符串编码为字节old_bytes = old_str.encode('utf-8')new_bytes = new_str.encode('utf-8')# 查找并替换所有匹配的字节序列replaced_count = 0i = 0while i <= len(content_bytearray) - len(old_bytes):# 检查是否匹配if content_bytearray[i:i+len(old_bytes)] == old_bytes:# 替换字节content_bytearray[i:i+len(old_bytes)] = new_bytesreplaced_count += 1i += len(old_bytes) # 跳过已替换的部分else:i += 1if replaced_count == 0:print(f"未找到字符串 '{old_str}'")return False# 写回文件with open(filename, 'wb') as f:f.write(content_bytearray)print(f"成功替换 {replaced_count} 处 '{old_str}' -> '{new_str}'")return Trueexcept Exception as e:print(f"处理文件时出错: {e}")return Falsedef main():# 检查命令行参数if len(sys.argv) != 4:print("用法: python rep.py <文件名> <旧字符串> <新字符串>")print("示例: python rep.py file.bin \"1.4.1\" \"1.4.0\"")sys.exit(1)filename = sys.argv[1]old_str = sys.argv[2]new_str = sys.argv[3]# 执行替换success = replace_string_in_binary(filename, old_str, new_str)if not success:sys.exit(1)if __name__ == "__main__":main()
如果希望在旧版本测试的,可以使用这个程序。
python3 rep.py '/par/14/rusty_sheet.duckdb_extension' "1.4.1" "1.4.0"
成功替换 5 处 '1.4.1' -> '1.4.0'
root@66d4e20ec1d7:/par# ./duckdb140 -unsigned
DuckDB v1.4.0 (Andium) b8a06e4a22
Enter ".help" for usage hints.
D load '/par/14/rusty_sheet.duckdb_extension';
D from read_sheets('/par/file*xls',header=0);
┌─────────────┐
│ A │
│ varchar │
├─────────────┤
│ Hello World │
│ Hello World │
└─────────────┘
这个版本插件最大的变化有两点,第一点:支持通配符处理多个电子表格;第二点:支持更多种类电子表格,包括旧版xls文件,ODS、wps et文件等。
上哪去找那么多种电子表格?我从calamine的源代码包中找到了一些,位于calamine/tests/。
先看这个插件包括的函数
select function_name from duckdb_functions() where function_name like '%sheet%';
┌────────────────┐
│ function_name │
│ varchar │
├────────────────┤
│ read_sheet │
│ read_sheets │
│ analyze_sheet │
│ analyze_sheets │
函数名一目了然,分析读取单个、多个电子表格文件。
下面举例
1.分析单表单个sheet, 用analyze_sheet,
例如
from analyze_sheet('/par/2025work.xlsx',sheet_name='证书',header=1);
┌─────────────┬─────────────┐
│ column_name │ column_type │
│ varchar │ varchar │
├─────────────┼─────────────┤
│ 8日 │ varchar │
│ B │ varchar │
│ C │ varchar │
如果用通配符,那么只有第一个满足条件的sheet被分析
from analyze_sheet('/par/2025work.xlsx',sheet_name='*',header=1);
┌──────────────────────────┬─────────────┐
│ column_name │ column_type │
│ varchar │ varchar │
├──────────────────────────┼─────────────┤
│ 工单创建时间 │ varchar │
│ 工单办结时间 │ varchar │
2.分析单表多个sheet, 用analyze_sheets ,
from analyze_sheets('/par/2025work.xlsx',sheets=['证书','工单实录'],header=1);
┌────────────────────┬────────────┬──────────────────────────┬─────────────┐
│ file_name │ sheet_name │ column_name │ column_type │
│ varchar │ varchar │ varchar │ varchar │
├────────────────────┼────────────┼──────────────────────────┼─────────────┤
│ /par/2025work.xlsx │ 工单实录 │ 工单创建时间 │ varchar │
│ /par/2025work.xlsx │ 工单实录 │ 工单办结时间 │ varchar │
│ · │ · │ · │ · │
│ /par/2025work.xlsx │ 证书 │ J │ varchar │
│ /par/2025work.xlsx │ 证书 │ K │ varchar │
虽然analyze_sheets也可以用来分析单个sheet, 但是有冗余列,不推荐
from analyze_sheets('/par/2025work.xlsx',sheets=['证书'],header=1);
┌────────────────────┬────────────┬─────────────┬─────────────┐
│ file_name │ sheet_name │ column_name │ column_type │
│ varchar │ varchar │ varchar │ varchar │
├────────────────────┼────────────┼─────────────┼─────────────┤
│ /par/2025work.xlsx │ 证书 │ 8日 │ varchar │
│ /par/2025work.xlsx │ 证书 │ B │ varchar │
│ /par/2025work.xlsx │ 证书 │ C │ varchar │
3.读取单文件多个sheet, 用read_sheets
通配符读取所有满足条件的sheet
from read_sheets('/par/400.xls',sheets=['*']);
┌──────────────────────┬─────────┬─────────┬────────────────┬
│ 400热线分日工单情况 │ B │ C │ D │
│ varchar │ varchar │ varchar │ varchar │
├──────────────────────┼─────────┼─────────┼────────────────┼
│ 分日工单情况 │ NULL │ NULL │ 分类型工单数量 │
│ 日期 │ 星期 │ 接起量 │ 口令\n问题 │
│ 2025-01-01 │ 元旦 │ 11 │ 5 │
│ · │ · │ · │ · │
│ 2025-12-29 │ 周五 │ 0 │ NULL │
│ 2025-12-30 │ 周六 │ 0 │ NULL │
│ 2025-12-31 │ 周日 │ 0 │ NULL │
│ 合计 │ NULL │ 0 │ 0 │
├──────────────────────┴─────────┴─────────┴────────────────┴
│ 401 rows (40 shown)
也可以用明确的sheet名列表
from read_sheets('/par/400.xls',sheets=['一月','二月']);
┌──────────────────────┬─────────┬─────────┬────────────────┬
│ 400热线分日工单情况 │ B │ C │ D │
│ varchar │ varchar │ varchar │ varchar │
├──────────────────────┼─────────┼─────────┼────────────────┼
│ 分日工单情况 │ NULL │ NULL │ 分类型工单数量 │
│ 日期 │ 星期 │ 接起量 │ 口令\n问题 │
│ 2025-01-01 │ 元旦 │ 11 │ 5 │
│ · │ · │ · │ · │
│ 2025-02-28 │ 周五 │ 198 │ 17 │
│ 合计 │ NULL │ 2637 │ 406 │
├──────────────────────┴─────────┴─────────┴────────────────┴
│ 65 rows (40 shown)
4.读取多文件多个sheet, 用read_sheets,列标题按第一个sheet的列标题给出,缺少的列用NULL填充
.system ls ???.xls
conv.xls demo.xls file.xls gbk2.xls
from read_sheets('/par/????.xls',sheets=['*'],header=1);
┌──────────────────────┬──────────────────────┬──────────────────────┬──────────────────────┬──────────────────────────┐
│ id │ title │ inserted_at │ updated_at │ mapping │
│ varchar │ varchar │ varchar │ varchar │ varchar │
├──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────────┤
│ e6535268-e25a-4620… │ 把下列句子翻译成英语 │ 2025-01-23T14:10:3… │ 2025-01-23T14:48:2… │ Length too long! │
│ 74c16f63-675c-4bc8… │ 货车运输成本最小化… │ 2025-01-27T13:56:2… │ 2025-01-27T14:06:5… │ Length too long! │
│ · │ · │ · │ · │ · │
│ 23 │ "doubly quoted str… │ 'singly quoted str… │ NULL │ NULL │
│ 24 │ Formula: │ The quick brown fox │ 2 │ NULL │
│ 1 │ Tom │ 12.4 │ NULL │ NULL │
│ 2 │ Jerry │ 3 │ NULL │ NULL │
│ 3 │ 张一 │ -12 │ NULL │ NULL │
├──────────────────────┴──────────────────────┴──────────────────────┴──────────────────────┴──────────────────────────┤
│ 281 rows (40 shown) 5 columns │