1 个稳定版本
1.0.0 | 2023年11月11日 |
---|
#612 in 文本处理
99KB
1.5K SLoC
TWAS - 随机文本替换应用程序和库
在 ${holiday} 前的夜晚,整座 ${building} 中没有任何生物在动,甚至连 ${{id: animal, aan: true}} 都没有。
在万圣节前夜的夜晚,整个 barn 中没有任何生物在动,甚至连 elephant 都没有。
TWAS(Text With Arbitrary Substitutions 的缩写)是一个文本替换工具,用于用随机选择的列表中的随机词/短语查找表中的项替换标识符,如 ${animal}
。
关于
twas 是一个随机文本替换工具,用于生成随机故事、游戏事件和其他杂项。给定一个文本提示和一个或多个查找表,twas 将用查找表中的随机文本替换任何 ${...}
提示。如果选定的文本也包含 ${...}
提示,则这些也将被替换(递归)。这样,twas 可以从几个简单的表中生成复杂的故事或描述。
如何安装
安装 twas
应用程序
假设您已在计算机上安装了 cargo,只需运行以下命令即可安装(或更新)twas CLI 应用程序
cargo install twas --features=app
安装 twas
库
在您的 Cargo.toml
文件中,只需将以下内容添加到依赖项部分
[dependencies]
twas="1"
用法
要使用 twas,您必须首先定义一个或多个随机查找表,以便在替换文本中引用。查找表可以是以下格式之一(格式细节在以下随机查找表格式部分描述):纯文本 (.txt)、逗号分隔值 (.csv)、JSON (.json) 和 YAML (.yml 或 .yaml)。可以一次性加载多个文件。您还可以包含一个目录,twas 将递归扫描支持文件格式并将其加载,使用相对于提供目录的文件路径作为前缀。也可以包含 .zip 文件,它们将被视为目录。
例如,这是一个简单的随机查找表,包含一组动物列表:animal.txt
antelope
bee
cat
dog
elephant
flying fish
然后,如果您运行 twas -i animal.txt "I have a pet ${animal}."
,文本 ${animal}
将被从 animal.txt
中随机选择的一行替换,并将结果打印到终端。文本替换语法和选项在以下文本替换语法和选项部分描述。
文本替换语法和选项
文本替换的目标是通过一个 $
美元符号后跟 {}
括号来识别,括号内包含要使用的查找表的 ID 或包含更多高级选项的 JSON 对象。例如,在文本字符串 "I have a pet ${animal}."
中的 ${animal}
或在文本字符串 "My pet is ${ {id: animal, aan: true} }."
中的 ${ {id: animal, aan: true} }
。
随机查找表 ID
要引用查找表,您需要指定其ID。对于.txt文件,ID就是文件名,不带.txt后缀(例如,animal.txt
的ID是animal
,可以用于文本替换,如${animal}
)。对于.csv文件,ID是文件名(不带.csv文件后缀),后面跟一个/
反斜杠和列名,例如CSV文件zoo-animals.csv
中列bird
的ID是zoo-animals/bird
,可以用于文本替换,如${zoo-animals/plural}
)。对于JSON和YAML文件,查找表可以嵌套,并且与.csv文件类似,层级用/
反斜杠分隔,使用不带后缀的文件名作为基础,例如包含以下内容的JSON文件plant.json
:{"trees": {"evergreen": ["pine", "cedar"]}}
包含IDplant/trees/evergreen
。查找ID区分大小写,不允许包含$
或@
。有关特定文件格式查找ID的详细信息,请参阅下面的相关随机查找表格式子部分。
如果您在ID前加上@
(例如 ${@fav-pet}
),则该ID被视为引用ID,此时它将重用之前的替换而不是从查找表中获取。例如:我有一只宠物 ${animal@fav-pet} 和一只宠物 ${animal}. ${@fav-pet} 是我最喜欢的。
在第一句话中保存了第一次替换作为引用ID fav-pet
,然后在第二句话中重用它。有关引用的使用,请参阅下面的引用部分。
基本替换语法
要在文本中进行简单的文本替换,只需在文本中添加一个紧跟的美元符号$
,后面紧跟一对大括号{}
,其中包含您要用于替换的查找表ID,例如${ look-up ID }
。ID前后的大白字符(例如空格)将被忽略,因此${animal}
和${ animal }
被视为相同。
要将生成的替换文本保存为以后重用的参考,请附加@
后跟参考ID(例如${animal@fav-pet}
)。要使用已保存的参考,只需使用@
和参考ID。例如
我有一只宠物 ${animal@fav-pet} 和一只宠物 ${animal}. 我最喜欢的宠物是 ${@fav-pet}
=> 我有一只狗和一只猫. 狗是我的最爱.
有关使用参考的详细说明,请参阅下面的参考部分。
高级替换语法
文本替换还可以使用双大括号和JSON或YAML语法进行指定,其中查找表ID通过JSON/YAML对象的id
字段提供。例如,${{id: animal}}
与${animal}
完全等价。twas提供了通过JSON语法应用文本替换的多个额外选项。选项如下
id(必需)
用于此替换的查找表的ID。如果ID以@
开头,则被视为引用ID。ID字段还可以包含使用$
进行ID替换,以将ID的一部分替换为保存的引用。例如,在文本我有一只宠物 ${{id: animal@pet}}.
中,如果${animal@pet}
替换为dog
,那么${{id: "names/$pet"}}
变为${{id: "names/dog"}}
,然后从names/dog
查找表中替换一个条目。有关引用使用的详细说明,请参阅下面的参考部分。
示例
我有一只宠物 ${{id: animal}}.
=> 我有一只宠物狗.
计数
count
选项允许您从查找表中选取多个条目。您可以指定一个数字或使用RPG骰子表示法(例如,“1d6+1”)从查找表中随机选取一定数量的条目。count
选项通常与sep: ", "
和last-sep: " and "
一起使用,以生成逗号分隔的列表。另请参阅method
、prefix
和suffix
。
示例
我有一只宠物 ${{id: animal, count: 2}}.
=>我有一只宠物狗猫.
我的宠物: ${{id: animal, count: 2, sep: ", ", last-sep: " and "}}.
=>我的宠物: 狗和猫.
我的宠物: ${{id: animal, count: 3, sep: ", ", last-sep: " and "}}.
=>我的宠物: 狗, 猫和猫.
我的宠物: ${{id: animal, count: 3, sep: ", ", last-sep: " and ", method: shuffle}}.
=>我的宠物: 狗, 猫和鸟.
方法
method
选项指定在启用 count
选项抽取多个项目时,从查找表中抽取所使用的随机查找算法。支持的方法有 "random" 和 shuffle。使用 "random" 时,同一项目可能连续多次被抽取。使用 shuffle 时,除非 count
大于查找表中的项目总数,否则同一项目不会再次被抽取。默认方法为 "random"。
示例
我的宠物: ${{id: animal, count: 3, sep: ", ", last-sep: " and "}}.
=>我的宠物: 狗, 猫和猫.
我的宠物: ${{id: animal, count: 3, sep: ", ", last-sep: " and ", method: shuffle}}.
=>我的宠物: 狗, 猫和鸟.
分隔符
使用 count
选项时,提供的 sep
字符串被放置在每个项目之间。如果没有指定,默认值是一个空格字符。通常与 sep: ", "
和 last-sep: " and "
结合使用,以创建逗号分隔的列表。另请参阅 method
、prefix
和 suffix
。
示例
宠物列表: ${{id: animal, count: 2, sep: ", "}}.
=> 宠物列表: dog, cat.
last-sep
类似于上述的 sep
,但仅放置在最后一个和倒数第二个项目之间
示例
我的宠物: ${{id: animal, count: 3, sep: ", ", last-sep: " and ", method: shuffle}}.
=>我的宠物: 狗, 猫和鸟.
前缀
prefix
被添加到从随机查找表中抽取的每个项目之前。这在制作带有 count
选项的随机列表时特别有用。
示例
我的宠物:${{id:animal,计数: 2, "前缀": "\n * "}}
=>
My pets:
* dog
* cat
后缀
suffix
被添加到从随机查找表中抽取的每个项目的末尾。这在通过 count
选项以结构化格式(如HTML)输出随机列表时特别有用。
示例
我的宠物:<br><ul>${{id:animal,计数: 2, "前缀": "\n<li>", "前缀": "</li>"}}</ul>
=>
My pets:<br><ul>
<li>dog</li>
<li>cat</li></ul>
case
case
选项修改替换文本的大小写。通常假设随机查找表中的文本都是小写。支持的 case
值包括
case | description | 示例 |
---|---|---|
original | 无变化(默认) | big blue 3D glasses |
upper | 全部大写字母 | BIG BLUE 3D GLASSES |
lower | 全部小写 | big blue 3d glasses |
title | 每个单词的首字母大写 | Big Blue 3D Glasses |
first | 仅首字母大写 | Big blue 3D glasses |
ref
如果你使用 ref
选项,随机选择的项将从随机查找表中保存以供重新使用,并使用提供的引用ID。请参阅下面的 引用 部分以了解关于引用的详细描述。
示例
我有一只宠物 ${{id: animal, ref: "fav-pet"}} 和一只宠物 ${{id: animal}}. ${@fav-pet} 是我最喜欢的.
=> 我有一只宠物狗和一只宠物猫. 狗是我的最爱.
hidden
如果设置为 true,则 hidden
防止替换出现在文本中。这仅在与上面描述的 ref
选项结合使用时才有效,在这种情况下,隐藏的替换被保存为变量以供稍后文本中使用。
示例
${{id: animal, ref: pet, "hidden": true}}I have a pet ${{id: "@pet"}}.
=> I have a pet dog.
aan
如果将 aan
设置为 true,则在不定冠词 a
或 an
前添加不定冠词,根据随机选择的查找表中的项的拼写进行适当添加。
示例
My favorite animal is ${{id: animal, aan: true}}.
=> My favorite animal is a dog.
参考
当您想在多个地方使用相同的结果时,您可以使用引用来保存生成的结果并重新使用。例如,假设您正在创建一个关于从 animal
查找表中随机选择的宠物的故事。由于故事多次引用了相同的宠物,您只想从 animal
随机查找表中抽取一次。为了实现这一点,您将第一个 animal
使用保存为引用 pet
,然后您在任何需要使用相同引用的地方,指定 @pet
作为 ID 而不是 animal
。因此,您的故事文本可能如下所示 "I have a pet ${animal@pet}. ${{id: "@pet", aan: true, "case": "first"}} is a good animal to have as a pet. I love my ${@pet}!"
,如果 ${animal@pet}
解析为 dog
,则变为 "I have a pet dog. A dog is a good animal to have as a pet. I love my dog!"
。
创建引用
如果使用基本替换语法,您可以通过在查找表ID后直接跟一个参考ID来创建一个引用,例如 ${animal@pet}
从 animal
查找表中随机选择一个条目,并将结果保存为参考ID pet
。如果使用JSON语法,则使用 ref
选项将结果保存到指定的参考ID,例如 ${{id: animal, ref: pet}}
。
使用引用进行文本替换
引用通过提供的参考ID存储,参考ID的使用方式类似于查找表ID,但前面有一个 @
前缀。例如,${@pet}
将被替换为保存的 pet
引用,同样,${{id: "@pet"}}
也是如此。
如果使用JSON语法,您仍然可以对引用的文本应用额外的选项,例如 aan
或 case
。
使用引用进行ID替换
您可以用保存的引用的值替换ID字符串的一部分。在这种情况下,您使用 $
后跟参考ID作为查找ID的一部分(例如 ${pet-names/$pet}
)。这允许您使用一个随机查找表的结果来决定使用哪个其他查找表。
例如,假设您加载以下 animal.txt
文件
dog
cat
bird
rat
并加载以下 pet-names.csv
文件
bird,cat,dog,rat
pip,paws,spot,whiskers
seed,mew,spike,cheesy
lala,claws,rolf,nibbler
然后您可以使用选择的动物来选择特定的名称,例如:My pet ${animal@pet}'s name is ${{id: "pet-names/$pet", "case": "title"}}.
=> My pet dog's name is Spot.
使用骰子符号进行随机数
您还可以使用RPG骰子标记将随机数字插入到文本中。数字替换以#
井号符号开始,后跟包含骰子表达式的{}
花括号,例如#{1d6+2}
将被替换为一个介于3到8之间的随机数(表达式"1d6+2"表示"掷一个6面的骰子并加2")。有关支持的骰子表达式语法的更多详细信息,请参阅dicexp crate。
随机查找表格式
支持多种格式来定义随机查找表。支持的格式在此处详细描述。
.txt
在.txt
文件中的每一行都将被解析为一个查找表条目,所有可能的值具有相等的权重。
IDs
此查找表的ID就是该文件名,不带.txt
扩展名(例如,animal.txt
的ID是animal
)。
示例
以下是一个ID为animal
的随机查找示例,它以相等的概率随机选择dog
、cat
、bird
或rat
:animal.txt
dog
cat
bird
rat
.csv
.csv
文件被解释为标准的逗号分隔值(CSV)文件(UTF-8编码),其中第一行是标题行,包含列名,所有后续行是每个列的可能值。每个列都是自己的随机查找表。所有行具有相等的概率,除非存在名为weight
的列。如果存在weight
列,则每行的概率将按相应的weight
列中的十进制值加权。
IDs
CSV文件中每列的ID是filename/column
(例如,在pet-names/dog
文件中,列dog
的ID是pet-names/dog
)。
示例
以下示例创建了四个不同的随机查找表,ID分别为pet-names/bird
、pet-names/cat
、pet-names/dog
和pet-names/rat
:pet-names.csv
bird,cat,dog,rat
pip,paws,spot,whiskers
seed,mew,spike,cheesy
lala,claws,rolf,nibbler
以下示例创建了一个ID为rarity/rarity
的查找表,有60%的概率抽取"普通",30%的概率抽取"不常见",9%的概率抽取"稀有",1%的概率抽取"非常稀有":rarity.csv
weight,rarity
6,common
3,uncommon
0.9,rare
0.1,very rare
.yaml(和.yml)
YAML文件可以包含一个或多个随机查找表,具有任意层级的嵌套深度。在YAML文件中遇到的任何列表都将被解析为查找表,所有项目具有相同的概率,而使用字符串-数字映射来指定加权概率(例如rarity: {common: 6, uncommon: 3, rare: 0.9, "very rare": 0.1}
)。表可以通过嵌套的映射对象来组织,每个嵌套都会为查找表ID路径添加一个层级。
IDs
YAML文件中每个随机查找的ID等于文件名(不包含文件后缀)后跟文件中查找表的对象-映射路径。
在简单的只包含列表的文件的情况下(见以下示例中的animal.yaml
),ID只是文件名。对于文件底层的对象,ID将是filename/name
(见以下示例中的treasure.yaml
),而嵌套对象的ID将使用反斜杠分隔符表示嵌套路径,即filename/level1/level2/.../name
,见以下colors.yaml
示例)。
示例
以下是一个ID为animal
的随机查找示例,该查找随机解析为bird
、cat
、dog
或rat
中的任何一个,具有相同的概率:animal.yaml
- bird
- cat
- dog
- rat
以下示例创建了一个查找表,创建了两个ID为treasure/money
和treasure/junk
的表,其中treasure/money
中的条目具有不同的概率,但treasure/junk
中的所有条目都具有相同的概率:treasure.yaml
money:
"100 copper pennies": 4
"10 silver dollars": 1.5
"1 gold ingot": 0.5
junk:
- old boot
- pocket lint
- broken toy boat
以下示例创建了三个ID为colors/light/primary
、colors/paint/primary
和colors/paint/secondary/pastel
的查找表:colors.yaml
light:
primary:
- red
- green
- blue
paint:
primary:
- red
- yellow
- blue
secondary:
pastel:
- peach
- light green
- lavender
.json
JSON文件的工作方式与YAML完全相同(见上文)。
IDs
与上述YAML解析相同。
示例
以下是一个ID为animal
的随机查找示例,该查找随机解析为bird
、cat
、dog
或rat
中的任何一个,具有相同的概率:animal.json
[
"bird",
"cat",
"dog",
"rat"
]
以下示例创建了一个查找表,创建了两个ID为treasure/money
和treasure/junk
的表,其中treasure/money
中的条目具有不同的概率,但treasure/junk
中的所有条目都具有相同的概率:treasure.json
{
"money": {
"100 copper pennies": 4,
"10 silver dollars": 1.5,
"1 gold ingot": 0.5
},
"junk": [
"old boot",
"pocket lint",
"broken toy boat"
]
}
以下示例创建三个查找表,其ID分别为:colors/light/primary
、colors/paint/primary
、colors/paint/secondary/pastel
:colors.json
{
"light": {
"primary": [
"red",
"green",
"blue"
]
},
"paint": {
"primary": [
"red",
"yellow",
"blue"
],
"secondary": {
"pastel": [
"peach",
"light green",
"lavender"
]
}
}
}
目录
当你加载一个目录时,twas会递归地扫描目录中所有支持的文件格式并加载所有这些文件。所有加载的文件的ID都将以其在加载目录中的相对文件路径为前缀。因此,如果你加载目录foo/bar
,文件foo/bar/animal.txt
将具有ID animal
,但文件foo/bar/vehicles/cars.txt
将具有ID vehicles/cars
。
IDs
目录中加载的文件的ID将以其在加载目录中的相对子目录路径为前缀。
示例
给定以下目录结构和上面的文件示例
resources/
├─ adventure/
│ ├─ rarity.csv
│ └─ treasure.yaml
├─ colors.yaml
└─ pets/
├─ animal.txt
└─ pet-names.csv
则加载resources
目录将注册以下随机查找表ID列表
adventure/rarity/rarity
adventure/treasure/money
adventure/treasure/junk
colors/light/primary
colors/paint/primary
colors/paint/secondary/pastel
pets/animal
pets/pet-names/bird
pets/pet-names/cat
pets/pet-names/dog
pets/pet-names/rat
.zip
当twas加载一个.zip
文件时,它会提取它并将它的内容视为目录(见上面)。
IDs
加载到zip存档文件中的文件的ID将以其在加载zip存档中的相对子目录路径为前缀。
示例
给定以下zip存档结构和上面的文件示例
resources.zip
├─ adventure/
│ ├─ rarity.csv
│ └─ treasure.yaml
├─ colors.yaml
└─ pets/
├─ animal.txt
└─ pet-names.csv
则加载resources
目录将注册以下随机查找表ID列表
adventure/rarity/rarity
adventure/treasure/money
adventure/treasure/junk
colors/light/primary
colors/paint/primary
colors/paint/secondary/pastel
pets/animal
pets/pet-names/bird
pets/pet-names/cat
pets/pet-names/dog
pets/pet-names/rat
许可和再分发
twas源代码受Mozilla Public License,v. 2.0条款约束。
依赖项
~12–24MB
~335K SLoC