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