牙套

类似bash的大括号展开,在JavaScript中实现。比其他大括号扩展库更安全,完全支持Bash 4.3大括号规范,而不牺牲速度。
请考虑跟随这个项目的作者, Jon Schlinkert,并考虑主演该项目,以展示您的: 心: 和支持。
安装
安装与 npm:
$ npm install --save braces
v3.0.0发布! !
看到 更新日志 获取详细信息。
为什么要用大括号?
大括号模式通过添加匹配特定范围和字符序列的能力,使globs更加强大。
- 准确的 -完全支持 Bash 4.3扩展括号 规范(通过所有Bash大括号测试)
- 快速高效 -开始快,跑得快 良好的可扩展性 随着模式复杂性的增加。
- 有组织的代码库 解析器和编译器在出现边缘情况时易于维护和更新。
- 经过良好测试的 -数以千计的测试断言,并通过所有的Bash, minimatch,和 brace-expansion 单元测试(截至撰写本文的日期)。
- 更安全的 -你不应该担心用户定义攻击性或恶意的大括号模式,这会破坏你的应用程序。brace采取措施防止恶意的regex被用于DDoS攻击(参见 灾难性的回溯).
- 支持列表 -(又名“集合”)
a/{b,c}/d= >['a/b/d', 'a/c/d'] - 支持序列 -(又名“ranges”)
{01..03}= >['01', '02', '03'] - 支持措施 -(又名“增量”)
{2..10..2}= >['2', '4', '6', '8', '10'] - 支持转义 -防止特殊字符的求值。
用法
主导出是一个接受一个或多个大括号的函数 patterns 和 options。
const braces = require('braces');
// braces(patterns[, options]);
console.log(braces(['{01..05}', '{a..e}']));
//=> ['(0[1-5])', '([a-e])']
console.log(braces(['{01..05}', '{a..e}'], { expand: true }));
//=> ['01', '02', '03', '04', '05', 'a', 'b', 'c', 'd', 'e']
大括号展开与编译
默认情况下,大括号模式被编译成针对创建正则表达式和匹配进行优化的字符串。
编译
console.log(braces('a/{x,y,z}/b'));
//=> ['a/(x|y|z)/b']
console.log(braces(['a/{01..20}/b', 'a/{1..5}/b']));
//=> [ 'a/(0[1-9]|1[0-9]|20)/b', 'a/([1-5])/b' ]
扩大
来启用大括号展开 expand 选项设置为true,或使用 braces.expand () (返回与Bash类似的数组,或者 echo {1..5},或 minimatch):
console.log(braces('a/{x,y,z}/b', { expand: true }));
//=> ['a/x/b', 'a/y/b', 'a/z/b']
console.log(braces.expand('{01..10}'));
//=> ['01','02','03','04','05','06','07','08','09','10']
列表
展开列表(像Bash的“set”):
console.log(braces('a/{foo,bar,baz}/*.js'));
//=> ['a/(foo|bar|baz)/*.js']
console.log(braces.expand('a/{foo,bar,baz}/*.js'));
//=> ['a/foo/*.js', 'a/bar/*.js', 'a/baz/*.js']
序列
扩展字符范围(如Bash "sequences"):
console.log(braces.expand('{1..3}')); // ['1', '2', '3']
console.log(braces.expand('a/{1..3}/b')); // ['a/1/b', 'a/2/b', 'a/3/b']
console.log(braces('{a..c}', { expand: true })); // ['a', 'b', 'c']
console.log(braces('foo/{a..c}', { expand: true })); // ['foo/a', 'foo/b', 'foo/c']
// supports zero-padded ranges
console.log(braces('a/{01..03}/b')); //=> ['a/(0[1-3])/b']
console.log(braces('a/{001..300}/b')); //=> ['a/(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)/b']
看到 fill-range 对于所有可用的范围扩展选项。
Steppped范围
步进或增量可以与范围一起使用:
console.log(braces.expand('{2..10..2}'));
//=> ['2', '4', '6', '8', '10']
console.log(braces('{2..10..2}'));
//=> ['(2|4|6|8|10)']
当 .optimize 方法,或 options.optimize 设置为true,则将序列传递给 到正则表达式范围 为扩张。
嵌套
大括号模式可以嵌套。每个扩展字符串的结果不排序,并保持从左到右的顺序。
“扩大”括号
console.log(braces.expand('a{b,c,/{x,y}}/e'));
//=> ['ab/e', 'ac/e', 'a/x/e', 'a/y/e']
console.log(braces.expand('a/{x,{1..5},y}/c'));
//=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
“优化”括号
console.log(braces('a{b,c,/{x,y}}/e'));
//=> ['a(b|c|/(x|y))/e']
console.log(braces('a/{x,{1..5},y}/c'));
//=> ['a/(x|([1-5])|y)/c']
逃离
逃离括号
如果出现以下情况,则不会展开或计算大括号模式 左括号或右括号被转义:
console.log(braces.expand('a\\{d,c,b}e'));
//=> ['a{d,c,b}e']
console.log(braces.expand('a{d,c,b\\}e'));
//=> ['a{d,c,b}e']
逃离逗号
大括号内的逗号也可以转义:
console.log(braces.expand('a{b\\,c}d'));
//=> ['a{b,c}d']
console.log(braces.expand('a{d\\,c,b}e'));
//=> ['ad,ce', 'abe']
单项
遵循bash约定,大括号模式在包含单个字符时也不会展开:
console.log(braces.expand('a{b}c'));
//=> ['a{b}c']
选项
options.maxLength
类型: Number
默认的: 10,000
描述限制输入字符串的长度。当输入字符串被生成或者你的应用程序允许用户传递一个字符串时很有用,等等。
console.log(braces('a/{b,c}/d', { maxLength: 3 })); //=> throws an error
options.expand
类型: Boolean
默认的: undefined
描述:生成一个“展开的”大括号模式(或者你可以使用 braces.expand() 方法,它做同样的事情)。
console.log(braces('a/{b,c}/d', { expand: true }));
//=> [ 'a/b/d', 'a/c/d' ]
options.nodupes
类型: Boolean
默认的: undefined
描述:从返回的数组中移除重复项。
options.rangeLimit
类型: Number
默认的: 1000
描述:为防止用户传递恶意模式,当 braces.expand() 是用来或 options.expand 为true,生成的范围将超过 rangeLimit。
你可以自定义 options.rangeLimit 或者设置为 Inifinity 完全禁用它。
例子
// pattern exceeds the "rangeLimit", so it's optimized automatically
console.log(braces.expand('{1..1000}'));
//=> ['([1-9]|[1-9][0-9]{1,2}|1000)']
// pattern does not exceed "rangeLimit", so it's NOT optimized
console.log(braces.expand('{1..100}'));
//=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100']
options.transform
类型: Function
默认的: undefined
描述:自定义范围扩展。
示例:转换非数值
const alpha = braces.expand('x/{a..e}/y', {
transform(value, index) {
// When non-numeric values are passed, "value" is a character code.
return 'foo/' + String.fromCharCode(value) + '-' + index;
},
});
console.log(alpha);
//=> [ 'x/foo/a-0/y', 'x/foo/b-1/y', 'x/foo/c-2/y', 'x/foo/d-3/y', 'x/foo/e-4/y' ]
示例:转换数值
const numeric = braces.expand('{1..5}', {
transform(value) {
// when numeric values are passed, "value" is a number
return 'foo/' + value * 2;
},
});
console.log(numeric);
//=> [ 'foo/2', 'foo/4', 'foo/6', 'foo/8', 'foo/10' ]
options.quantifiers
类型: Boolean
默认的: undefined
描述:在正则表达式中,可以使用限定词来指定一个标记可以重复多少次。例如, a{1,3} 将与字母匹配 a 一到三次。
不幸的是,正则表达式量词恰好与 Bash列表
这个 quantifiers 选项告诉大括号何时检测 regex量词 在给定模式中定义的,而不是试图将它们展开为列表。
例子
const braces = require('braces');
console.log(braces('a/b{1,3}/{x,y,z}'));
//=> [ 'a/b(1|3)/(x|y|z)' ]
console.log(braces('a/b{1,3}/{x,y,z}', { quantifiers: true }));
//=> [ 'a/b{1,3}/(x|y|z)' ]
console.log(braces('a/b{1,3}/{x,y,z}', { quantifiers: true, expand: true }));
//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
options.keepEscaping
类型: Boolean
默认的: undefined
描述:不要从结果中去掉用于转义的反斜杠。
什么是“支撑扩张”?
大括号展开是unix shell中流行的一种参数展开,用于生成字符串列表,以及与通配符(globs)一起使用时的类正则表达式匹配。
除了“展开”,大括号也用于匹配。换句话说:
关于支撑扩展的更多信息 (点击展开)
大括号扩展主要有两种类型:
- 列表它们是用花括号内的逗号分隔值定义的:
{a,b,c} - 序列:它们是用起始值和结束值定义的,用两个点分隔:
a{1..3}b. 可选地,可以传递第三个参数来定义要使用的“step”或增量:a{1..100..10}b. 这些有时也被称为“范围”。
下面是一些示例大括号模式来说明它们是如何工作的:
集
{a,b,c} => a b c
{a,b,c}{1,2} => a1 a2 b1 b2 c1 c2
序列
{1..9} => 1 2 3 4 5 6 7 8 9
{4..-4} => 4 3 2 1 0 -1 -2 -3 -4
{1..20..3} => 1 4 7 10 13 16 19
{a..j} => a b c d e f g h i j
{j..a} => j i h g f e d c b a
{a..z..3} => a d g j m p s v y
结合
集合和序列可以混合在一起或与任何其他字符串一起使用。
{a,b,c}{1..3} => a1 a2 a3 b1 b2 b3 c1 c2 c3
foo/{a,b,c}/bar => foo/a/bar foo/b/bar foo/c/bar
事实上,大括号可以从相对简单的模式中“展开”,这使得它们非常适合快速生成测试fixture、文件路径和类似的用例。
括号匹配
除了…之外 扩张,大括号模式对于执行类似正则表达式的匹配也很有用。
例如,模式 foo/{1..3}/bar 将匹配以下任何字符串:
foo/1/bar
foo/2/bar
foo/3/bar
而不是:
baz/1/qux
baz/2/qux
baz/3/qux
大括号也可以与 一团模式 执行更高级的通配符匹配。例如,模式 */{1..3}/* 将匹配以下任何字符串:
foo/1/bar
foo/2/bar
foo/3/bar
baz/1/qux
baz/2/qux
baz/3/qux
支架匹配缺陷
虽然大括号模式提供了一种用户友好的匹配字符串范围或字符串集的方式,但也有一些主要的缺点和潜在的风险,您应该意识到。
tldr
“撑炸弹”
- 支撑扩展会消耗大量的处理资源
- 随着括号模式的增加 尺寸呈线性,扩展模式所需的系统资源呈指数级增长
- 用户可能会意外(或故意)耗尽系统资源,导致相当于DoS攻击(好处:不需要编程知识!)
有关更详细的示例解释,请参阅 几何复杂性 部分。
解决方案
跳转到 性能部分 看看与其他库相比,大括号是如何解决这个问题的。
几何复杂性
至少,集合限制为两个元素的括号模式具有二次或 O(n^2) 的复杂性。但算法的复杂性随着集合数量的增加呈指数增长, 每个集合的元素,增加,即 O(n^c)。
例如,下面的集合演示了二次元(O(n^2)复杂性:
{1,2}{3,4} => (2X2) => 13 14 23 24
{1,2}{3,4}{5,6} => (2X2X2) => 135 136 145 146 235 236 245 246
但是在集合中加入一个元素,我们得到一个n倍笛卡尔积 O(n^c) 复杂性:
{1,2,3}{4,5,6}{7,8,9} => (3X3X3) => 147 148 149 157 158 159 167 168 169 247 248
249 257 258 259 267 268 269 347 348 349 357
358 359 367 368 369
现在,想象一下如果每个元素都是n元组,复杂度会如何增长:
{1..100}{1..100} => (100X100) => 10,000 elements (38.4 kB)
{1..100}{1..100}{1..100} => (100X100X100) => 1,000,000 elements (5.76 MB)
尽管这些例子显然是人为的,但它们展示了大括号模式是如何迅速失控的。
更多的信息
有兴趣了解更多关于大括号展开的知识吗?
表演
大括号不仅速度快,而且比其他大括号扩展库更准确。
更好的算法
幸运的是,有一个解决方案 “支撑炸弹”问题: 当使用大括号模式进行匹配时,不要将其展开为数组。
相反,将模式转换为优化的正则表达式。这说起来容易做起来难,而且目前只有大括号库能做到这一点。
数字就是证据
随着模式复杂性的增加,Minimatch的速度会呈指数级增长,而大括号则不会。生成以下结果 braces() 和 minimatch.braceExpand(),分别。
| 模式 | 牙套 | [minimatch] [] |
|---|---|---|
{1..9007199254740991}[^ 1] | 298 B (5 459μs女士) | N / A(冻结) |
{1..1000000000000000} | 41 B (1女士15μs) | N / A(冻结) |
{1..100000000000000} | 40 B (890μs) | N / A(冻结) |
{1..10000000000000} | 39 B (2 49μs女士) | N / A(冻结) |
{1..1000000000000} | 38 B (608μs) | N / A(冻结) |
{1..100000000000} | 37 B (397μs) | N / A(冻结) |
{1..10000000000} | 35 B (983μs) | N / A(冻结) |
{1..1000000000} | 34 B (798μs) | N / A(冻结) |
{1..100000000} | 33 B (733μs) | N / A(冻结) |
{1..10000000} | 32 B (5 632μs女士) | 78.89 MB (16s 388ms 569μs) |
{1..1000000} | 31 B (1 381μs女士) | 6.89 MB (15秒496ms 887μs) |
{1..100000} | 30 B (950μs) | 588.89 kB (146 921μs女士) |
{1..10000} | 29 B (1 114μs女士) | 48.89 kB (14 187μs女士) |
{1..1000} | 28 B (760μs) | 3.89 kB (1 453μs女士) |
{1..100} | 22 B (345μs) | 291 B (196μs) |
{1..10} | 10 B (533μs) | 20 B (37μs) |
{1..3} | 7 B (190μs) | 5 B (μs) 27日 |
更快的算法
当您需要展开时,大括号仍然快得多。
生成以下结果 braces.expand() 和 minimatch.braceExpand()分别)
| 模式 | 牙套 | [minimatch] [] |
|---|---|---|
{1..10000000} | 78.89 MB (2s 698ms 642μs) | 78.89 MB (18s 601ms 974μs) |
{1..1000000} | 6.89 MB (458 576μs女士) | 6.89 MB (1s 491ms 621μs) |
{1..100000} | 588.89 kB (20 728μs女士) | 588.89 kB (156 919μs女士) |
{1..10000} | 48.89 kB (2 202μs女士) | 48.89 kB (13 641μs女士) |
{1..1000} | 3.89 kB (1 796μs女士) | 3.89 kB (1 958μs女士) |
{1..100} | 291 B (424μs) | 291 B (211μs) |
{1..10} | 20 B (487μs) | 20 B (72μs) |
{1..3} | 5 B (166μs) | 5 B (μs) 27日 |
如果你想自己进行比较,看看 测试/支持/ generate.js。
基准
运行基准测试
安装开发依赖项:
npm i -d && npm benchmark
最新的结果
在不牺牲性能的情况下,牙套更加精确。
● expand - range (expanded)
braces x 53,167 ops/sec ±0.12% (102 runs sampled)
minimatch x 11,378 ops/sec ±0.10% (102 runs sampled)
● expand - range (optimized for regex)
braces x 373,442 ops/sec ±0.04% (100 runs sampled)
minimatch x 3,262 ops/sec ±0.18% (100 runs sampled)
● expand - nested ranges (expanded)
braces x 33,921 ops/sec ±0.09% (99 runs sampled)
minimatch x 10,855 ops/sec ±0.28% (100 runs sampled)
● expand - nested ranges (optimized for regex)
braces x 287,479 ops/sec ±0.52% (98 runs sampled)
minimatch x 3,219 ops/sec ±0.28% (101 runs sampled)
● expand - set (expanded)
braces x 238,243 ops/sec ±0.19% (97 runs sampled)
minimatch x 538,268 ops/sec ±0.31% (96 runs sampled)
● expand - set (optimized for regex)
braces x 321,844 ops/sec ±0.10% (97 runs sampled)
minimatch x 140,600 ops/sec ±0.15% (100 runs sampled)
● expand - nested sets (expanded)
braces x 165,371 ops/sec ±0.42% (96 runs sampled)
minimatch x 337,720 ops/sec ±0.28% (100 runs sampled)
● expand - nested sets (optimized for regex)
braces x 242,948 ops/sec ±0.12% (99 runs sampled)
minimatch x 87,403 ops/sec ±0.79% (96 runs sampled)
关于
贡献
拉请求和明星总是受欢迎的。对于bug和特性请求, 请创建一个问题。
运行测试
运行和审查单元测试是熟悉库及其API的好方法。可以使用以下命令安装依赖项并运行测试:
$ npm install && npm test
建立文档
(这个项目的自述。Md由 动词请不要直接编辑自述文件。对自述文件的任何更改都必须在 .verb.md 自述文件模板)。
使用实例生成自述文件。
$ npm install -g verbose/verb#dev verb-generate-readme && verb
贡献者
| 提交 | 贡献者 |
|---|---|
| 197 | jonschlinkert |
| 4 | doowb |
| 1 | es128 |
| 1 | eush77 |
| 1 | hemanth |
| 1 | wtgtybhertgeghgtwtg |
作者
Jon Schlinkert
许可证
版权所有©2019 Jon Schlinkert。 根据 麻省理工学院的许可。
该文件由 动词-生成-自述文件, v0.8.0,于2019年4月8日发布。