novelWriter 文档文件格式深入解析:以 Mr. Nobody 人物笔记为例剖析存储结构与标记语法

📅 发布时间:2026/10/14 20:32:26
novelWriter 文档文件格式深入解析:以 Mr. Nobody 人物笔记为例剖析存储结构与标记语法
桌面应用【免费下载链接】novelWriternovelWriter is an open source plain text editor designed for writing novels项目地址https://gitcode.com/gh_mirrors/no/novelWriter点击查看免费下载导读本文以 novelWriter 官方示例项目tests/_lipsum中的一个人物笔记文件 tests/_lipsum/content/4c4f28287af27.md 为解剖样本逐层拆解 novelWriter 的文档文件格式从 TOML 元数据头、tag/plot引用行、%Short注释修饰符到 52 位随机句柄、nwProject.nwx项目结构与索引重建机制。读完本文你将能完全读懂任何 novelWriter 项目中的.md文档理解标签与引用在底层如何关联并掌握手动编辑文档后的正确补救流程。一、文件定位这是一个什么样的人物笔记在tests/_lipsum目录下保存着一整套完整的 novelWriter 示例项目项目名Lorem Ipsum作者 lipsum.com见 tests/_lipsum/nwProject.nwx。项目结构中共有 22 个条目分属Novel正文、Characters人物、Plot剧情、World世界、Trash回收站五个根节点。目标文件4c4f28287af27.md在项目 XML 中注册为item handle4c4f28287af27 parent67a8707f2f249 root67a8707f2f249 order0 typeFILE classCHARACTER layoutNOTE meta expandedno headingH1 charCount1864 wordCount284 paraCount3 cursorPos70 / name statussbaa94f importid6b1d0 activeyesMr. Nobody/name /item也就是说这是一个挂在Characters根节点67a8707f2f249下的、类别为CHARACTER、布局为NOTE的人物笔记文件。它对应项目树中名为Mr. Nobody的条目而文档内部标题则是Nobody Owens取自尼尔·盖曼小说《坟场之书》的主人公名用于演示标签引用关系。二、TOML 元数据头文件自描述的身份证文档开头是被包裹的 TOML 格式元数据块共 8 个字段这是 novelWriter 当前版本的文档头格式 name Mr. Nobody parent 67a8707f2f249 handle 4c4f28287af27 class CHARACTER layout NOTE textHash 3bea02c22084574351f3cf43e7af5b6110d996e8 createdDate Unknown updatedDate 2026-08-01 21:51:21 各字段含义与写入逻辑对应如下字段示例值说明nameMr. Nobody文档在项目树中的显示名称对应 XML 中的nameparent67a8707f2f249父级条目句柄这里指向 Characters 根节点handle4c4f28287af27文档自身句柄即文件名主体也是 XML 中item的 handle 属性classCHARACTER条目类别对应 nwItemClass 枚举NOVEL / PLOT / CHARACTER / WORLD / TIMELINE / OBJECT / ENTITY / CUSTOM 等layoutNOTE条目布局只有两种DOCUMENT正文文档与NOTE项目笔记见 nwItemLayout 枚举textHash3bea02c2…正文部分不含元数据头的 SHA-1 哈希用于检测文件是否被外部修改createdDateUnknown创建时间旧文件迁移时可能缺失而显示UnknownupdatedDate2026-08-01 21:51:21最近一次保存时间仅当正文哈希变化时才更新这些字段并非手写而是由 novelwriter/core/document.py 中的ProjectDocument.writeDocument()第 229 行在每次保存时自动生成写入前先计算正文 SHA-1 哈希哈希与上次不同则刷新updatedDate文件不存在则同时写入createdDate元数据头通过NTomlParser(flatTrue)序列化拼装为\n…\n\n前缀再写入.tmp临时文件成功后replace覆盖原文件先写临时文件再替换保证断电时旧数据不丢失读取时由_splitHeader()第 372 行识别开头的行最多读取MAX_META_LINES 20行作为元数据其余内容视为正文。值得注意storage 格式文档 docs/source/technical/storage.rst 提到文档扩展名为.nwd但当前源码如quickReadText()中的content / f{tHandle}.md与tests/_lipsum/content/目录下的实际文件均使用.md扩展名说明 2.x 系列已统一为.md。三、正文标记语法标题、标签、引用与注释去掉元数据头后正文是一段纯文本共由四类语法元素组成3.1 标题行# Nobody Owens#前缀表示一级标题H1。novelWriter 支持的标题层级由 novelwriter/text/formats.py 的processHeading()第 70 行解析前缀层级说明#H1章节标题Part 级##H2章标题Chapter 级###H3场景标题Scene 级####H4小节标题Section 级#!/##!/###!H1/H2/H3带标记隐藏标题用于小说封面页、献词、序言等不进入正文统计的文档示例项目中即大量使用了#!与##!变体例如 tests/_lipsum/content/7a992350f3eb6.md 的#! Lorem Ipsum和 tests/_lipsum/content/88d59a277361b.md 的##! Prologue。每个文档只允许一个一级标题索引据此划分各标题下的字数统计区间。3.2 标签行tag: Bod | Nobody Owenstag是 novelWriter 的关键字行语法用于定义标签Tag并建立跨文档引用。本文件中该行声明了两个标签Bod与Nobody Owens同一行可用|分隔多个值。关键字行在 novelwriter/constants.py 的nwKeyWords类第 190 行中统一定义完整的 12 个关键字为关键字作用关联类别tag定义标签仅此关键字有定义能力—pov视角人物引用CHARACTERfocus焦点人物引用CHARACTERchar出场人物引用CHARACTERplot剧情线引用PLOTtime时间线引用TIMELINElocation地点引用WORLDobject物件引用OBJECTentity实体引用ENTITYcustom自定义引用CUSTOMstory故事线引用NOVELmention提及引用不建立反向索引—标签与引用的工作方式是在笔记里用tag定义标签在正文文档里用pov/plot/location等引用它。以本项目为例人物笔记定义了Bod标签而场景文件 tests/_lipsum/content/88243afbe5ed8.md 中写着### Scene One pov: Bod plot: Main location: Europe %Synopsis: …这样编辑器就能把场景中的视角人物Bod反查回Mr. Nobody笔记并在引用面板中展示双向关联。关键字行要求写在标题之后、正文之前pov: Bod这种引用写法要求被引用的标签已经存在于某个笔记的tag行中否则会提示未定义标签。3.3 注释修饰符行%Short: The Hero以%开头的行是注释行novelWriter 的注释体系比普通 Markdown 更丰富。processComment()novelwriter/text/formats.py 第 52 行支持如下修饰符语法类型用途%Short: 文本SHORT文档摘要如本文件的The Hero可显示在项目树/大纲中%Synopsis: 文本SYNOPSIS场景/章节情节摘要显示在编辑区底部预览%Note.Key: 文本NOTE带键的笔记注释如%Note.Purpose: Introduction%Footnote.f9kgf: 文本FOOTNOTE配合正文[footnote:f9kgf]标记的脚注定义%Story: 文本STORY剧情线说明% 普通注释PLAIN无修饰符的普通注释如示例中的% Exctracted from the lipsum.com website.%~ 忽略行IGNORE完全忽略脚注键由索引自动生成genNewNoteKey()novelwriter/core/index.py 第 1032 行用style[:1] 4 位随机字符生成形如f9kgf的 5 字符键。在 tests/_lipsum/content/88d59a277361b.md 中可以同时看到[footnote:f9kgf]行内标记与%Footnote.f9kgf:定义行。3.4 正文段落其余行即普通正文段落本文件中是三段 lipsum.com 生成的拉丁占位文本。除此之外示例项目还展示了其他正文语法[NEW PAGE]分页符tests/_lipsum/content/8c58a65414c23.md、 文本 居中标记与[field:allWords]动态字段tests/_lipsum/content/7a992350f3eb6.md、以 Tab 缩进表示引用块tests/_lipsum/content/846352075de7d.md等。四、句柄与存储布局文档为什么叫4c4f28287af27文件名4c4f28287af27并非随机乱码而是文档的句柄handle。根据 docs/source/technical/storage.rst 的说明句柄是一个52 位随机数以十六进制字符串表示——52 位恰好对应 13 个十六进制字符与本文件名的长度一致。采用这种加密式命名的原因有三规避不同操作系统对文件名的命名限制文件名与文档在项目中的显示名称解耦改名不影响文件路径便于配合版本控制与文件同步工具。一个 novelWriter 项目的物理布局如下项目文件夹/ ├── nwProject.nwx # 主项目文件结构、设置、元数据XML缩进格式化 ├── nwProject.lock # 项目锁文件 ├── ToC.txt # 文档目录表标签与存储位置的对照 ├── content/ # 所有文档正文即 handle.md └── meta/ # 半重要数据 ├── index.json # 项目索引缓存可重建 ├── builds.json # 手稿构建定义 ├── options.json # 各文档 GUI 选项缓存 ├── userdict.json # 拼写检查自定义词表 ├── sessions.jsonl # 写作会话统计JSON Lines └── views.json主项目文件nwProject.nwx中的content节点记录了全部 22 个条目及其父子关系、状态status、重要性import、标题层级与字数统计。当前项目文件格式版本为 1.6历史版本演进记录在 novelwriter/core/projectxml.py 第 54-67 行1.0 为原始格式1.1 引入统一content文件夹替代早期data_X分目录1.3 将布局缩减为 DOCUMENT/NOTE 两种1.5 起改为更紧凑的属性式条目存储并加入id属性本示例项目即为 fileVersion 1.5。五、索引与标签关联底层如何把人物笔记串起来文档被保存时novelWriter 会重新扫描并更新项目索引见 novelwriter/core/index.py 的Index类。索引保存四类核心信息每个文档每个标题层级的位置与字数统计文档与笔记中出现的所有标签所有引用行的关联关系谁引用了谁脚注等注释键。索引的持久化文件是meta/index.json其结构可参考测试基准文件 tests/_reference/coreIndex_LoadSave_tagsIndex.json。该文件属可丢失、可重建级别从Tools菜单即可手动重建整个索引。如果手动编辑了content/下的文档重新打开项目时必须重建索引否则编辑器无法感知外部改动——这正是 storage 文档给出的两条明确警告不应自行向项目文件夹添加文件可以谨慎地编辑content/下的纯文本但编辑nwProject.nwx可能使项目不可读甚至崩溃。索引还负责检测文档被外部修改ProjectDocument.writeDocument()会先比对文件 mtime/size再比对 SHA-1 哈希发现外部改动且未强制写入时会拒绝保存并置hashError标志见 novelwriter/core/document.py 第 250-265 行。六、占位文本与测试这个样例项目从哪来tests/_lipsum项目是自动化测试的固定样本fixture其正文段落取自 novelwriter/assets/text/lipsum.txt。编辑器内置的Insert Placeholder Text工具novelwriter/tools/lipsum.py 的GuiLipsum正是从该文件读取文本对话框允许设置 1100 段默认 5 段并选择是否随机打乱顺序确认后生成段落1\n\n段落2…的文本插入光标处。对应的 GUI 测试 tests/tools/test_lipsum.py 验证了三件事无项目时无法打开该工具设置段落数后可生成包含Lorem ipsum的文本随机模式下生成的文本非空并能通过nwDocInsert.LIPSUM正确插入到当前文档光标位置。这也解释了为什么示例笔记的正文全部是拉丁占位文本——它们本身就是用于测试和演示的数据。七、小结读懂一个 novelWriter 文档的四步法回到开头的4c4f28287af27.md现在可以完整还原它的身份一份CHARACTER 类别、NOTE 布局的人物笔记TOML 头声明了它的名称、父节点、句柄与哈希#标题定义文档内标题tag行定义Bod、Nobody Owens两个标签供正文场景以pov引用plot: Main把人物挂到Main剧情线%Short行提供人物一句话摘要textHash与updatedDate则保证了保存时的变更检测。这套元数据头 标题 标签/引用 注释修饰符 纯文本正文的组合就是 novelWriter 全部项目数据可被版本控制、可被外部工具处理、又能在应用内高效索引的根基。相关文件速查本文解剖样本tests/_lipsum/content/4c4f28287af27.md示例项目主文件tests/_lipsum/nwProject.nwx文档读写实现novelwriter/core/document.py存储格式官方文档docs/source/technical/storage.rst关键字与标签常量novelwriter/constants.py注释/标题语法解析novelwriter/text/formats.py项目索引实现novelwriter/core/index.py占位文本工具novelwriter/tools/lipsum.py 与 tests/tools/test_lipsum.py赞分享桌面应用【免费下载链接】novelWriternovelWriter is an open source plain text editor designed for writing novels项目地址https://gitcode.com/gh_mirrors/no/novelWriter点击查看免费下载相关推荐novelWriter 文档文件格式全解析以《Ancient Europe》世界设定笔记为例novelWriter 文档文件格式全解析以《Ancient Europe》世界设定笔记为例 导读 novelWriter 将每个项目文档以 handle.m桌面应用novelWriter 项目文档文件格式解析以 Alice in Wonderland 世界设定笔记为例novelWriter 项目文档文件格式解析以 Alice in Wonderland 世界设定笔记为例 导读 本文以开源小说写作软件 novelWrite桌面应用novelWriter 文档格式深度解析从 tests/_lipsum 样本看 NWD 文件结构与标记语法novelWriter 文档格式深度解析从 tests/_lipsum 样本看 NWD 文件结构与标记语法 novelWriter 是一个开源、无干扰的纯文本桌面应用上一篇在 Digital Ocean 上手动部署 Prisma 服务从 Droplet 创建到服务上线的完整实战下一篇Power BI 终极图标资源库让数据可视化更简单高效创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考