什么是数字?
我有一个简单的问题your“简单”问题:“一个数字”到底是什么意思?
- Is
−0
一个号码?
- 你对这个怎么看
√−1
?
- Is
⅝
or ⅔
一个号码?
- Is
186,282.42±0.02
英里/秒是一个数字——还是其中的两个或三个?
- Is
6.02e23
一个号码?
- Is
3.141_592_653_589
一个号码?怎么样π
, or ℯ
? And −2π⁻³ ͥ
?
- 中有多少个数字
0.083̄
?
- 中有多少个数字
128.0.0.1
?
- 什么数字是
⚄
抓住?怎么样⚂⚃
?
- Does
10,5 mm
里面有一个数字——还是有两个?
- Is
∛8³
一个数字——还是三个?
- 什么数字是
ↀↀⅮⅭⅭⅬⅫ AUC
代表2762还是2009?
- Are
४५६७
and ৭৮৯৮
数字?
- 关于什么
0377
, 0xDEADBEEF
, and 0b111101101
?
- Is
Inf
一个号码?是NaN
?
- Is
④②
一个号码?关于什么⓰
?
- 你对这个怎么看
㊅
?
- What do
ℵ₀
and ℵ₁
和数字有关系吗?或者ℝ
, ℚ
, and ℂ
?
建议模式
另外,你熟悉这些模式吗?您能解释一下各自的优缺点吗?
/\D/
/^\d+$/
/^\p{Nd}+$/
/^\pN+$/
/^\p{Numeric_Value:10}$/
/^\P{Numeric_Value:NaN}+$/
/^-?\d+$/
/^[+-]?\d+$/
/^-?\d+\.?\d*$/
/^-?(?:\d+(?:\.\d*)?|\.\d+)$/
/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/
/^((\d)(?(?=(\d))|$)(?(?{ord$3==1+ord$2})(?1)|$))$/
/^(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))$/
/^(?:(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}))$/
/^(?:(?:[+-]?)(?:[0123456789]+))$/
/(([+-]?)([0123456789]{1,3}(?:,?[0123456789]{3})*))/
/^(?:(?:[+-]?)(?:[0123456789]{1,3}(?:,?[0123456789]{3})*))$/
/^(?:(?i)(?:[+-]?)(?:(?=[0123456789]|[.])(?:[0123456789]*)(?:(?:[.])(?:[0123456789]{0,}))?)(?:(?:[E])(?:(?:[+-]?)(?:[0123456789]+))|))$/
/^(?:(?i)(?:[+-]?)(?:(?=[01]|[.])(?:[01]{1,3}(?:(?:[,])[01]{3})*)(?:(?:[.])(?:[01]{0,}))?)(?:(?:[E])(?:(?:[+-]?)(?:[01]+))|))$/
/^(?:(?i)(?:[+-]?)(?:(?=[0123456789ABCDEF]|[.])(?:[0123456789ABCDEF]{1,3}(?:(?:[,])[0123456789ABCDEF]{3})*)(?:(?:[.])(?:[0123456789ABCDEF]{0,}))?)(?:(?:[G])(?:(?:[+-]?)(?:[0123456789ABCDEF]+))|))$/
/((?i)([+-]?)((?=[0123456789]|[.])([0123456789]{1,3}(?:(?:[_,]?)[0123456789]{3})*)(?:([.])([0123456789]{0,}))?)(?:([E])(([+-]?)([0123456789]+))|))/
我怀疑上面的一些模式may满足您的需求。但我无法告诉你是哪一个或哪一个——或者,如果没有,则为你提供另一个——因为你还没有说出“数字”的含义。
如你所见,有一个巨大的数字可能性的数量:事实上,很可能有 ℵ₁ 的价值。 ☺
建议模式的关键
下面列出的每个编号说明描述了上面列出的相应编号模式的模式。
- 如果有则匹配非数字字符串中的任何位置,包括空格(如换行符)。
- 仅当字符串仅包含数字时才匹配,尾随换行符可能除外。请注意,数字被定义为具有属性“常规类别十进制数”,该属性可用作
\p{Nd}
, \p{Decimal_Number}
, or \p{General_Category=Decimal_Number}
。这实际上只是那些数字类型类别为 Decimal 的代码点的反映,可用作\p{Numeric_Type=Decimal}
.
- 这与大多数正则表达式语言中的 2 相同。 Java 是一个例外,因为它不映射简单的 charclass 转义符,例如
\w
and \W
, \d
and \D
, \s
and \S
, and \b
or \B
到适当的 Unicode 属性中。这意味着您不能对 Java 中的任何 Unicode 数据使用这八个单字符转义中的任何一个,因为它们仅适用于 ASCII,即使 Java 始终在内部使用 Unicode 字符。
- 这与 3 略有不同,它不限于十进制数,而是可以是任何数字;那是,
任何带有
\pN
, \p{Number}
, or \p{General_Category=Number}
财产。这些包括\p{Nl}
or \p{Letter_Number}
对于诸如罗马数字之类的东西\p{No}
or \p{Other_Number}
用于下标和下标数字、分数和带圆圈的数字 - 除其他外,例如计数棒。
- 这只匹配那些完全由十进制值为 10 的数字组成的字符串,所以像
Ⅹ
罗马数字十,以及⑩
, ⑽
, ⒑
, ⓾
, ❿
, ➉
, and ➓
.
- 仅那些包含缺少数值 NaN 的字符的字符串;换句话说,所有字符都必须具有某个数值。
- 仅匹配十进制数字,可以选择带有前导连字符减号。
- 与 7 相同,但现在如果符号是加号而不是减号也可以使用。
- 查找十进制数字,带有可选的连字符减号和可选的句号以及后面零个或多个十进制数字。
- 与 9 相同,但如果点后有数字,则不需要点前有数字。
- C 和许多其他语言的标准浮点表示法,允许使用科学记数法。
- 查找仅由任何脚本的两位或更多小数按降序排列组成的数字,例如 987 或 54321。此递归正则表达式包括对 Perl 代码的调用,该代码检查前瞻数字是否具有当前数字的后继代码点值;也就是说,它的序数值大一。可以在 PCRE 中使用 C 函数作为标注来完成此操作。
- 这将查找有效范围内具有四个十进制数字的有效 IPv4 地址,例如 128.0.0.1 或 255.255.255.240,但不是 999.999.999.999。
- 这会查找有效的 MAC 地址,即六个冒号分隔的两个 ASCII 十六进制数字对。
- 这会查找 ASCII 范围内带有可选前导符号的整数。这是匹配 ASCII 整数的正常模式。
- 这与 15 类似,只是需要用逗号分隔三组。
- 这与 15 类似,只是用于分隔组的逗号现在是可选的。
- 这是匹配 ASCII 中的 C 风格浮点数的正常模式。
- 这类似于 18,但需要用逗号来分隔 3 组,并且以 2 为基数,而不是以 10 为基数。
- 这就像 19,但是是十六进制的。请注意,可选指数现在由 G 而不是 E 表示,因为 E 是有效的十六进制数字。
- 这会检查字符串是否包含 C 样式浮点数,但有一个可选的分组分隔符,每三位数字之间有一个逗号或下划线(LOW LINE)。它还将该字符串存储到
\1
捕获组,使其可用为$1
比赛成功后。
来源和可维护性
模式编号 1,2,7–11 来自 Perl 的早期版本经常问的问题列出问题“如何验证输入?”。该部分已被建议使用正则表达式::常见 http://search.cpan.org/perldoc?Regexp::Common模块,编写者Abigail http://search.cpan.org/~abigail/ and 达米安·康威 http://search.cpan.org/~dconway/。原始模式仍然可以在食谱 2.1 中找到。Perl 食谱 http://oreilly.com/catalog/9780596003135,“检查字符串是否为有效数字”,可以为令人眼花缭乱的多种语言找到解决方案,包括 ada、common lisp、groovy、guile、haskell、java、merd、ocaml、php、pike、python、 rexx、ruby 和 tclPLEAC项目 http://pleac.sourceforge.net/.
模式 12 可以重写得更清晰
m{
^
(
( \d )
(?(?= ( \d ) ) | $ )
(?(?{ ord $3 == 1 + ord $2 }) (?1) | $ )
)
$
}x
It uses 正则表达式递归 https://stackoverflow.com/questions/4031112/regular-expression-matching/4034386#4034386,它存在于许多模式引擎中,包括 Perl 和所有 PCRE 派生语言。但它也使用嵌入的代码标注作为其第二个条件模式的测试;据我所知,代码标注仅在 Perl 和 PCRE 中可用。
模式 13-21 源自前面提到的 Regexp::Common 模块。请注意,为了简洁起见,这些内容都没有包含您在生产代码中肯定需要的空格和注释。这可能是这样的/x
mode:
$real_rx = qr{ ( # start $1 to hold entire pattern
( [+-]? ) # optional leading sign, captured into $2
( # start $3
(?= # look ahead for what next char *will* be
[0123456789] # EITHER: an ASCII digit
| [.] # OR ELSE: a dot
) # end look ahead
( # start $4
[0123456789]{1,3} # 1-3 ASCII digits to start the number
(?: # then optionally followed by
(?: [_,]? ) # an optional grouping separator of comma or underscore
[0123456789]{3} # followed by exactly three ASCII digits
) * # repeated any number of times
) # end $4
(?: # begin optional cluster
( [.] ) # required literal dot in $5
( [0123456789]{0,} ) # then optional ASCII digits in $6
) ? # end optional cluster
) # end $3
(?: # begin cluster group
( [E] ) # base-10 exponent into $7
( # exponent number into $8
( [+-] ? ) # optional sign for exponent into $9
( [0123456789] + ) # one or more ASCII digits into $10
) # end $8
| # or else nothing at all
) # end cluster group
) }xi; # end $1 and whole pattern, enabling /x and /i modes
从软件工程的角度来看,中使用的风格仍然存在几个问题/x
模式版本就在上面。首先,有大量的代码重复,你会看到相同的内容[0123456789]
;如果其中一个序列不小心遗漏了一个数字,会发生什么?其次,您依赖于必须计算的位置参数。这意味着你可能会写这样的内容:
(
$real_number, # $1
$real_number_sign, # $2
$pre_exponent_part, # $3
$pre_decimal_point, # $4
$decimal_point, # $5
$post_decimal_point, # $6
$exponent_indicator, # $7
$exponent_number, # $8
$exponent_sign, # $9
$exponent_digits, # $10
) = ($string =~ /$real_rx/);
坦率地说,这是令人憎恶的!编号很容易出错,很难记住符号名称在哪里,而且写起来很乏味,尤其是当您不需要所有这些部分时。将其重写为使用命名组而不仅仅是编号组。同样,我将使用 Perl 语法作为变量,但模式的内容应该适用于支持命名组的任何地方。
use 5.010; # Perl got named patterns in 5.10
$real_rx = qr{
(?<real_number>
# optional leading sign
(?<real_number_sign> [+-]? )
(?<pre_exponent_part>
(?= # look ahead for what next char *will* be
[0123456789] # EITHER: an ASCII digit
| [.] # OR ELSE: a dot
) # end look ahead
(?<pre_decimal_point>
[0123456789]{1,3} # 1-3 ASCII digits to start the number
(?: # then optionally followed by
(?: [_,]? ) # an optional grouping separator of comma or underscore
[0123456789]{3} # followed by exactly three ASCII digits
) * # repeated any number of times
) # end <pre_decimal_part>
(?: # begin optional anon cluster
(?<decimal_point> [.] ) # required literal dot
(?<post_decimal_point>
[0123456789]{0,} )
) ? # end optional anon cluster
) # end <pre_exponent_part>
# begin anon cluster group:
(?:
(?<exponent_indicator> [E] ) # base-10 exponent
(?<exponent_number> # exponent number
(?<exponent_sign> [+-] ? )
(?<exponent_digits> [0123456789] + )
) # end <exponent_number>
| # or else nothing at all
) # end anon cluster group
) # end <real_number>
}xi;
现在抽象被命名了,这很有帮助。您可以按名称提取组,并且只需要您关心的组。例如:
if ($string =~ /$real_rx/) {
($pre_exponent, $exponent_number) =
@+{ qw< pre_exponent exponent_number > };
}
为了使其更易于维护,还需要做一件事来实现此模式。问题是仍然有太多重复,这意味着在一个地方很容易改变,但在另一个地方却很难改变。如果您正在进行 McCabe 分析,您会说它的复杂性指标太高了。我们大多数人都会说它太缩进了。这使得很难跟上。为了解决所有这些问题,我们需要一种“语法模式”,一种带有定义块来创建命名抽象的模式,然后我们将其视为稍后在比赛中的子例程调用。
use 5.010; # Perl first got regex subs in v5.10
$real__rx = qr{
^ # anchor to front
(?&real_number) # call &real_number regex sub
$ # either at end or before final newline
##################################################
# the rest is definition only; think of ##
# each named buffer as declaring a subroutine ##
# by that name ##
##################################################
(?(DEFINE)
(?<real_number>
(?&mantissa)
(?&abscissa) ?
)
(?<abscissa>
(?&exponent_indicator)
(?&exponent)
)
(?<exponent>
(&?sign) ?
(?&a_digit) +
)
(?<mantissa>
# expecting either of these....
(?= (?&a_digit)
| (?&point)
)
(?&a_digit) {1,3}
(?: (?&digit_separator) ?
(?&a_digit) {3}
) *
(?: (?&point)
(?&a_digit) *
) ?
)
(?<point> [.] )
(?<sign> [+-] )
(?<digit_separator> [_,] )
(?<exponent_indicator> [Ee] )
(?<a_digit> [0-9] )
) # end DEFINE block
}x;
See how 好得多语法模式比原来的线路噪音模式更好?获得正确的语法也容易得多:我输入的内容甚至没有一个需要纠正的正则表达式语法错误。 (好吧,我输入的所有其他内容也没有任何语法错误,但我已经这样做了一段时间了。:)
语法模式看起来更像 BNF,而不是人们讨厌的丑陋的旧正则表达式。它们更容易阅读、编写和维护。所以我们不要再有丑陋的模式了,好吗?