Watch at Ease看见世界,读懂生活。

科技 · 实用指南

将 Audacity 频谱标签转为 CSV,避免频率与标签错位

把频率续行配到对应标签,保留原秒数字符串,并区分频率记录缺失、边界未定义和真正的零赫兹。

把标签时间行与其频率续行配对的原创示意图
原创模拟数据示意图:频率续行与前一个标签合为一条 CSV 记录。 Watch at Ease

频谱标签导出不一定是一条标签对应一行表格。Audacity 扩展文本格式把时间与标签文字写在一行,已存频率范围写在下一条以反斜杠开头的续行。把每行都当新标签会破坏配对。

原生扩展导出已能保留频率。本指南补充离线 CSV 转换,适用于自己生成的受支持 UTF-8 文本,并检查配对、保留原数字文本;它不分析音频,也不能恢复导出中缺失的频率。

导出正确文本,核对范围

保留项目和原标签文本。选择纯文本标签导出,不选 SRT、WebVTT 或音频导出。你的版本若提供“导出标签样式”,选择带频率范围的扩展格式。检查一个已知频谱标签:时间与文字行后应接反斜杠、制表符、低频值、制表符、高频值。各版菜单用词不同;我们未运行当前界面。

核对导出包含哪些标签轨。当前标签轨手册说明可从某条轨的菜单导出该轨,当前原始导出模型也支持选择轨道。旧文档曾描述合并各轨。不要为遵循过时办法而删除轨道。需要轨道归属时分别导出,并在 CSV 外记录项目、轨道、版本及日期。

没有频率续行不等于零赫兹:可能使用标准导出样式,也可能本来未保存频率范围。扩展格式中的 -1 表示该边界未定义。必要时检查原标签并重新导出;转换不能推断缺失范围,也不能重建文本中没有的轨道名称。

用 Python 3 转换受支持副本

此方法接收两个以制表符分隔的非负开始/结束秒数、可选标签文字及最多一条紧随其后的频率续行。支持小数点数字、有限非负频率或未定义标记 -1。负时间标签、逗号小数、空记录、多余制表符字段或未来新增续行格式均不在范围内,创建输出前就会停止。

在安装 Python 3 的 Linux Bash 终端,进入自己 labels.txt 副本所在目录。把以下命令按一行复制,保留直引号与反斜杠。保留输入名,换用新输出名;已有 labels.csv 会被拒绝。这条准确命令只在 Linux Bash 测试,未在 Windows/macOS 测试。

python3 -c 'exec('"'"'import csv\nfrom decimal import Decimal\nimport pathlib\nimport re\nimport sys\n\ndef number(text, minimum):\n if not re.fullmatch(r"[+-]?[0-9]+(?:\\.[0-9]*)?(?:[eE][+-]?[0-9]+)?", text):\n raise ValueError("Expected an unpadded dot-decimal number")\n value = Decimal(text)\n if not value.is_finite() or value < minimum:\n raise ValueError("Unsupported numerical value")\n return value\n\nrows = []\nraw = pathlib.Path(sys.argv[1]).read_text(encoding="utf-8-sig")\nlines = raw.split("\\n")\nif lines[-1] == "":\n lines.pop()\nfor line in lines:\n fields = line.split("\\t")\n if fields[0] == "\\\\":\n if len(fields) != 3 or not rows or rows[-1][-1] != "absent":\n raise ValueError("Orphan, repeated or unsupported frequency line")\n low, high = [number(text, Decimal(-1)) for text in fields[1:]]\n if any(value < 0 and value != -1 for value in [low, high]):\n raise ValueError("Negative frequency must be the undefined -1 sentinel")\n if low >= 0 and high >= 0 and low > high:\n raise ValueError("Reversed frequency bounds")\n rows[-1][4:] = [fields[1], fields[2], "present"]\n else:\n if len(fields) not in [2, 3]:\n raise ValueError("Expected two times and optional label, tab separated")\n start, end = [number(text, Decimal(0)) for text in fields[:2]]\n if end < start:\n raise ValueError("Reversed time bounds")\n rows.append([str(len(rows) + 1), fields[0], fields[1], fields[2] if len(fields) == 3 else "", "", "", "absent"])\nif not rows:\n raise ValueError("No label records")\nheader = ["ordinal", "start_seconds", "end_seconds", "label", "low_hz", "high_hz", "frequency_record"]\nwith open(sys.argv[2], "x", encoding="utf-8-sig", newline="") as output:\n writer = csv.writer(output)\n writer.writerow(header)\n writer.writerows(rows)\nwith open(sys.argv[2], encoding="utf-8-sig", newline="") as output:\n if list(csv.reader(output)) != [header] + rows:\n raise ValueError("Readback differs; discard output and use a new name")\nprint(len(rows), "label records checked; missing frequency is not zero")\n'"'"')' "labels.txt" "labels.csv"

阅读七列,保留各自含义

CSV 七列为 ordinal、start_seconds、end_seconds、label、low_hz、high_hz、frequency_record。ordinal 按输入顺序从 1 开始,不是轨道号。present 表示解析了实际频率续行;absent 表示两频率单元格留空。明确的 -1 保留原文字,不改成零或猜测的边界。

转换器保留标签文字、重复记录、输入顺序及原数字字符串。Decimal 只用于检查边界,不进行浮点舍入、时长计算、排序或频率估计。CSV 引号处理标签中的逗号与双引号。输出回读后逐字段与已解析记录比较,通过后才打印成功。

LibreOffice Calc 导入选择 UTF-8、逗号分隔及直双引号文字分隔符。七列均设为“文本”,关闭“合并分隔符”“修剪空格”“计算公式”。对话框被隐藏时启用“编辑筛选器设置”。保留未改 CSV 主副本;数值计算前另建工作副本,并在其中选择正确小数语言区域。

核对配对,弃用不完整结果

把打印的标签数与预期导出对照,不与物理行数对照:频率续行不是另一个标签。检查已知点标签、区域标签及单边未定义标签,特别核对缺失频带附近记录,避免后一个频率错接到其他标签。多个轨合并导出可能已丢失归属;此 CSV 不会恢复它。

我们用这条准确命令执行 23 项模拟案例,覆盖点/区域配对、缺失与零、-1 边界、长数字文本、Unicode 和标点、重复空标签以及 UTF-8 BOM/CRLF。六项有效案例又验证重复写入被拒绝且输出不变,输入字节保持不变。未运行 Audacity 或 Calc,也未在 Windows/macOS 运行此命令。

错误或孤立频率行、重复续行、边界倒置及不支持记录会在创建输出前停止。写入或回读失败时,弃用不完整 CSV,换新名称重跑。保留原文本作对照。此表记录已导出选择,不是峰值、振幅、物种识别或已完成音频导出;频率范围是保存的标注,不是新做的频谱测量。

区分频率记录缺失、边界未定义和零赫兹的原创示意图
原创示意图:缺失、明确的未定义 -1 和已记录的零是三种状态。 Watch at Ease