2.6符号-运算符

AutoHotkey可以说是一门非常注重符号运用的脚本语言了,在热键那节我们学习了修饰符,这节集中讲解下运算符,运算符有数学运算符、逻辑运算符、位运算符,还有很特别的%(百分号),让人疑惑的,(逗号),猜不出来具体意思的=(等号)……。

所谓符号呢,可以是图形图像、文字组合,也不妨是声音信号、建筑造型,甚至可以是一种思想文化、一个时事人物。例如“=”在数学中是等价的符号,“紫禁城”在政治上是中国古代皇权的象征。总的来说,符号的意思就是一种“特征纪念”,就像绰号是为了让人容易记住,方便辨认的称呼。你记张三李四可能麻烦,但你记“大胡子”、“小眼镜儿”就方便多了,所以符号也可以说是由人的认识习惯造成的。

autohotkey将符号用到了极致,灵活、奇妙。不过不熟悉的话,只能臣妾心里苦了。这节课呢我们就一起来领略一下autohotkey符号的风骚吧!

一、数学运算符 (:=、=、+=、-=、*=、/=、//=、.=、|=、&=、^=、>>=、<<= 、+、++、-、–、*、**、/、//)

+、-、*、/这四个符号属于加、减、乘、除的标准写法,这里不在赘述。

(一)赋值运算符:= 

先介绍其中相对好理解的 := 运算符。

:= 和其他语言中 = 用法基本一致,来看几个例子:

a := 123
b := 456.789
c := "AHK中文社区"
d := a
e := a . c

可以看到如果将变量赋值成整数、浮点数、字符串,直接写就好了。我们也可以把一个已经存在的变量赋值给一个新变量。这和其他语言中 = 的用法并无二致。.(点运算符)用来拼接字符串,这个在后续文章中也会单独介绍。

如果我们将一个不存在的变量赋值给一个新变量,像这样:

f := abc

那么 f 将是一个空字符串,也就是和这样效果一样:

f := ""

(二)运算符=(赋值和比较)

简单来说, ( = )赋值是为了字符串而生的,它的眼中没有表达式,它所赋值内容,作用规则与命令式的参数编辑办法 完全相同

解决字符串中变量引用的问题,有了( % )这个磨人的小妖精,这也是AHK新手纠结的主要难点。

例如:

Box = (5 = 5)

上例Box得到”(5 = 5)”这个字符串

Box := (5 = 5)

上例Box得到布尔值结果( 1 ),(:=) 赋值符号之后,此处(=)作为比较运算符使用。

Box = % (5 = 5)

上例Box同样得到( 1 )

这里说明一下( % )符号其中一个用法,当 放置于 ( = )赋值符号后首位,其后参数性质将等同于 ( := ) 赋值符号的应用规则

Box := % 表达式

( := )赋值这种写法无碍,就是多此一举

它近似于( := )符号后跟的所有编辑的代码都跟着( “” )双引号,但是( % )符号是原意,故无法在( “” )双引号字符串中使用( % )符号对格式转义或变量取值

( = ) 赋值的作用范围仅限于:

变量名 = ...

上例这样的单行格式开头,其余情况下(如if后接的表达式),(=) 符号皆视作比较运算符

它在多行段字符串变量赋值有不错的表现。格式如下。

Box =
(
行1字符串
行2字符串
)

这在( := )多行赋值得是另外的写法如下:

Box := "行1字符串"
. "`n行2字符串"

顺带一提,( “`n” )是 ( ` )转义符对 字母( n ) 的转义,类似c语言中(\n),表示换行符,单行代码欲赋值多行字符串时,两个赋值符的写法分别为:

Box := "行1字符串`n行2字符串"
Box = 行1字符串`n行2字符串

= 后边的内容全部被认为是字符串,看几个例子:

a = 123
b = 456.789
c = AHK中文社区
d = a
e = %a%
f = %a%456

注意这里边的 a 和 b 已经不是整数和浮点数了,而全是字符串。d 的值也不是 123,而是字符串 a。那么是不是 =
只能用来将一个字符串常量赋值给一个变量呢?如果这样的话,= 基本也没有存在的必要了。我们可以看下 e = %a%,里边有一个奇怪的 % 符号(%也是 AHK 中的大坑),%a% 的意思是取 a 变量的值,所以 e 的值是字符串 123,同理 f 的值是字符串123456。是不是 = 比 := 稍微难理解一些?

另外 = 还有一些高级用法,比如将多行文本赋值给一个变量:

a =
(
123
456
789
)

在括号中的多行文本会被赋值给 a,这在某些需要赋值大段文字的场景是非常方便的。

用 = 赋值空字符串也更加简洁:

a =

(其实 a := 这样用也是可以的,虽然看起来怪怪的。)

此外在某些场景只能用 = 赋值,比如处理命令行参数的场景:

argc = %0%
argv1 = %1%

(三)两种赋值方式的使用场景

既然 = 和 := 在多数情况都可以实现相同的功能,什么场景应该使用 =,什么场景应该使用 := 呢?

这个并没有明确的规定,可以因人而异,根据自己的习惯来。但尽量前后统一,以免给自己和别人带来麻烦。

一般来说,使用 := 是更加严谨的,这也是和其他编程语言的用法对齐,更易于被接受。但也并不是弃 = 而不用,在某些场景,比如赋值大段文字,= 还是非常方便的。

(四)赋值运算符拓展

+=、-=、*=、/=、//=、.=、|=、&=、^=、>>=、<<= 这些都是赋值的表达形式. 对变量的内容进行运算, 然后把结果保存到同一个变量中 。

+=、-=、*=和 /=运算符是用变量的值加、减、乘以或除以另一个值的一种简写形式。如,Var*=2Var:=Var*2 会得到相同的结果。

//=是向下舍除,例如Var //= 2执行向下舍除, 把 Var 除以 2, 然后把结果保存回 Var. 同样地, Var .= "abc"Var := Var . "abc" 的一种简写形式。

.、|、&、^、>>、<<这几个运算符在下文中讲解,他们与=连在一起使用的作用与上面的意义是一样的

注意:

  • 与其他大多数运算符不同, 赋值运算是从右往左执行的. 因此, Var1 := Var2 := 0 这个语句中首先把 0 赋值给 Var2, 然后把 Var2 赋值给 Var1。
  • 如果使用赋值运算的结果作为其他某些运算符的输出, 那么输入的值是变量自身. 例如, 如果变量 Var 新增值后大于 50, 那么表达式 (Var+=2) > 50 结果为真. 这样还允许赋值被作为 ByRef传递或获取它的地址, 例如:&(x:="abc")。
  • 需要避免语法错误或提供更直观的操作时, 会自动提升赋值运算符的优先级. 例如:not x:=y 等价于 not (x:=y). 同样地, ++Var := X 等价于 ++(Var := X);而 Z>0 ? X:=2 : Y:=2 等价于 Z>0 ? (X:=2) : (Y:=2)。

由向后兼容引起的已知限制(可能会在未来的版本中解决):

  1. 当 /= 为表达式中最左边的运算符并且它不是多语句表达式的一部分时, 而且输入都不是浮点数时, 它会执行向下舍除(在其他所有情况中, /= 会执行真除);
  2. 仅当 += 和 -= 为一行中最左边的运算符时, 它们才支持日期/时间的计算;
  3. 运算符 +=, -= 和 *= 仅在空变量在一行单独使用时才把空变量视为零; 例如, y:=1, x+=1 和 MsgBox % x-=3 当 x 为空时都得到空的结果。

(五)++、–、**、//

++、–是前置和后置的自增/自减. 从变量中增加或减去 1(但在 1.0.46 之前的版本中, 这些只能在一行上单独使用; 此行中不存在其他运算符). 运算符可以放在变量名的前面或后面. 如果放在变量名的 前面, 则立即执行自增/减运算并把结果用于下一运算. 例如, Var := ++X 让 X 自增后才把它的值赋给 Var. 相反地, 如果运算符放在变量名的 后面, 则在下一运算使用该变量 之后 才对其进行自增/减运算. 例如, Var := X++ 把 X 的当前值赋给 Var 后才进行自增. 由于向后兼容性, 仅当空变量在一行中单独使用时, 运算符 ++ 和 — 才把它们视为零; 例如, y:=1, ++x 和 MsgBox % ++x 当 x 为空时结果都为空. 该运算符用下面的例子来说明,

x=1
y=1
var1:=++x  ; 结果为 var1=2, x=2
var2:=y++  ; 结果为 var2=1, y=2
++z        ; 空变量单独使用
msgbox % z ; 结果为 z=1

幂(**). 底数和指数都可以为小数. 如果指数为负数, 即使底数和指数都为整数, 结果也会被格式化为浮点数. 因为 ** 的优先级高于一元负号, 所以 -2**2 的计算过程和 -(2**2) 一样且得到结果 -4. 因此, 要让负号的优先级高于幂运算, 需要把它们包围在括号中, 例如 (-2)**2.

注意:

  • 不支持底数为负数且指数为小数的情况, 例如 (-2)**0.5; 它会产生空字符串. 但 (-2)**2 和 (-2)**2.0 都是支持的. 该运算符用下面的例子来说明:
  • 与数学上的对应项不同, ** 在 AutoHotkey v1 中是左结合的. 例如, x ** y ** z 计算为 (x ** y) ** z.
msgbox % 2**-2     ; 结果为 0.250000
msgbox % 2**(-2)   ; 结果为 0.250000
msgbox % -2**2     ; 结果为 -4
msgbox % -(2**2)   ; 结果为 -4
msgbox % (-2)**2   ; 结果为 4
msgbox % (-2)**2.0 ; 结果为 4.000000
msgbox % (-2)**0.5 ; 结果为空

向下舍除(//): 如果两个输入都是整数, 那么双斜杠运算符使用高效的整数除法. 例如, 5//3 结果为 1 而 5//-3 结果为 -1. 如果任何一个输入为浮点数, 则执行浮点除法并把结果往下取整到最近的整数. 例如, 5//3.0 结果为 1.0 而 5.0//-3 结果为 -2.0. 尽管浮点除法的结果为整数, 但它被保存为浮点格式, 以便其他使用者能使用浮点格式. 关于求模运算, 请参阅 mod().

二、逻辑运算符:(>、<、!、=、==、!=、<>、|、||、&、&&)

(一) ! 取反符号, 逻辑非,真值转为假值,假值转为真值。

!和not是逻辑非的两种表达方式,not除了优先级较低外, 其他的与 ! 运算符相同. 例如, not (x = 3 or y = 3) 等同于 !(x = 3 or y = 3).

真转假: 将布尔真值计算为假0,如下:

!5
!"中文社区"

假转真: 将布尔假值计算为真1,如下:

!未赋值的变量
!""
!0

以上括号可省,仅为阅读便利用途, ! 取反符常用于开关逻辑

! 作用于右侧的单项值,它优先级在普通算数运算符之上(也在所有逻辑/比较运算符之上),not 运算符是同样作用,要留心not优先级在算数运算符之下。如:

!1 + 1 ;得 1
not 1 + 1 ;得 0

是优先级次序导致的计算顺序差异, 如下顺序:

!1 + 1
not (1+ 1)

故( ! )取反符号作用于长表达式时,用()可以解决这类麻烦。

!(1 + 1)

可以试着理解下面示例表达式/赋值语句,它初次被激发,变量(开关)是何值,再次激发是何值?

开关 := !开关

(二) && 逻辑与、 ||  逻辑或

&& 优先级高于 || ,这两个运算符根据左右两端布尔属性判断取值。

&& 表示该运算符2边都是真值为真(返回1), 有1项为假值则返回假(0),同运算符 and, 建议固定选用其中一种。and和&&是逻辑与的两种表达方式,例如:x > 3 and x < 10。要提高性能, 则要应用求值优化。注意:以 AND/OR/&&/|| (或其他任何运算符) 开始的行会自动 附加到前一行的末尾。

555&& "AutoHotkey中文社区" ;返回1
0 && 666       ;返回0

|| 两个分隔符表示两端有一项是真值即真(1), 两端都为假返回0,同运算符 or。

"中文社区" || 0   ;返回1
0 || ""          ;返回0

&& 或 || 连用性质与单独使用相仿, 不做赘述。

... && ... && ... && ...
... || ... || ... || ...

(三)>、<和=、==、<> (!=)比较运算符

== 说起,它严格匹配左右两端每一位字符,区别大小写。

 = 符号作用类似  == , 只不过 = 就字符串匹配忽略大小写差异。

a := "abc"
b := "ABC"

if (a = b)
{
    ; 条件成立
}

if (a == b)
{
    ; 条件不成立
}

2.5!= 和 <> 的用法(不等)

在纯数间判断差异不大

005 = 5      ;返回1
005 == 5     ;返回1
2.100 = 2.1  ;返回1
2.100 == 2.1 ;返回1

但是字符串与数型混用比较可能造成麻烦

"005" = 5     ;返回0
"005" == 5    ;返回0
"2.10" = 2.1  ;返回0
"2.10" == 2.1 ;返回0

是( 0 )的锅

"5" = 5       ;返回1
"5" == 5     ;返回1
"2.1" = 2.1   ;返回1
"2.1" == 2.1  ;返回1

 <> 与 != 是同样的用途,包括优先级相等,意思是 不等于,即符号两边值不相等时返回1

但是对字符串的比较没那么苛刻,将忽略大小写。

若需要严格区分大小写可以参考!(“abc” == “Abc”) 返回1,如:

("Abc" != "abc") . ("Abc" <> "abc") . (!("Abc" =="abc"))

此行表达式返回001,代表这三段表达式的运行结果。

其实大小写的区分还跟StringCaseSense命令有关。

我们需要先了解一个命令:

StringCaseSense, On|Off|Locale

StringCaseSense 用于设置在字符串处理时是否区分大小写。如果设置了 On,就是区分;如果设置了 Off,就是不区分。先不用关注 Locale 参数。默认是 Off。

注意这个命令不影响 = 和 == 的功能。也就是说即使设置了 StringCaseSense, On,用 = 比较字符串还是不区分大小写的。但影响 != 和 <> 的结果:

a := "abc"
b := "ABC"

StringCaseSense, On
if (a != b)
{
    ; 条件成立
}

StringCaseSense, Off
if (a != b)
{
    ; 条件不成立
}

这个也并不难理解,因为通常情况我们无需设置 StringCaseSense,比较的结果都是不区分大小写的。

> 大于号、 < 小于号、大于等于 (>=), 和 小于等于 (<=)这样的比较运算符仅用于数型值判断,要注意的是,它与一般双值运算符无异,是成对执行计算的。如果某个输入不是数字, 则按字母顺序比较 (加了引号的原义字符串例如 “55” 在这种情况中总是被当成是非数值的)。仅当命令StringCaseSense打开时, 比较才区分大小写。

如:

10 > 5 > 2

这个表达式在我们一般观念中似乎没有什么错误。

但实际它在AHK的运算流程是:

(10 > 5) > 2
(1) > 2
(0)

布尔相关的运算符计算结果为数型,参与算数表达式无碍,却容易在概念上混淆,无论是逻辑表达式,比较表达式,还是算数与真假值计算的运算符混用的表达式,日常编码中尽可能以()区别开来,如:

(10 > x) && (x > 2) 

这个表达式判断变量x是否在2至10这个区间的数值。

上面说到这类运算符常见于if判断,我们仍然可以将布尔性质的表达式赋值于变量中

由于字符串与数型数值界限暧昧,为避免两者交集的深坑,非必要情况请区别使用

重要说明:含有表达式的 if 语句与传统的 if 语句(例如 If FoundColor <> Blue),可以通过单词“if”后是否有开括号来区分。尽管通常把整个表达式包围在括号中,不过也可以写成这样:if (x > 0) and (y > 0)。此外, 如果单词 “if” 后的第一项为函数调用或类似 “not” 或 “!” 这样的运算符时, 开括号可以完全省略

Shift::
    if (Count<1 && A_TimeSincePriorHotkey<400 && A_PriorHotkey = A_ThisHotkey)
    {
        Count++
    }
    else
    {
        Count:=0
    }
    if Count>0
    {
        Run notepad
        Count:=0
    }
return

三、RegExMatch 的简写~=

~= 是对 RegExMatch 的简写。 例如, “abc123” ~= “\d” 会设置 ErrorLevel 为 0 并返回数字 4(首个数字的位置). 在 [v1.1.03] 之前, 此运算符和 等号 (=) 或 (:=) 运算符优先级相同, 但并未在文档中说明.
译者注, ~= 的优先级低于 not(!), 也就是说会先取反再匹配. 下面是一个很深的坑, 请测试下面这个错误的例子:

str := "d"
if (! str ~= "abc"){     	;~ 必须用括号包围才能获得预期的效果 if ! (str ~= "abc")
  Msgbox 无法匹配字母 %str%   	;~ 不会进入这里
} else {
  Msgbox 可以匹配字母 %str%	;~ 出乎意料的结果
}
    

再来测试, not(!) 与 := 相比优先级更低, 这是一个结果可以预料的例子:

if (!str := "a"){ 	;~ 效果同 if ! (str := "a")
  Msgbox 不正常: %str% 为 false
}else{
  Msgbox 正常: %str% 为 true	;~ 预期的效果
}
    

四、格式转义符号:百分号 (%)

AHK 中有一个很特别的符号 %。几乎随便从网上下一段 AHK 代码,就可以看到里边有 % 符号,而且 % 在 AHK 里的用法和所有其他编程语言都不一样,基本靠猜的话是猜不出来的。很多人觉得这是一个坑,其实深入理解后,也是一种巧妙的设计。

(一)用法一:%var%

第一种用法我们在介绍赋值表达式的时候接触过:

a = 123
b = %a%
c = %a%456

当用 = 对变量赋值时,如果一个字符串被两个 % 包围,并且中间没有空格,那么含义是取这个变量的值。

在调用一个命令时,也是这样。

a = 123
MsgBox, %a%

如果我们这样用,Name, xx, yy,Name 就是命令;如果我们这样用,Name(“xx”, “yy”),Name 就是函数。命令和函数的区别我们也会在之后的文章了解到,现在只需要关注命令即可。

注意两个%之间只能有一个单独的变量名,像 x[1]、x.y、x[y]、fun() 等一概不支持。

(二)用法二:% var

% 还有另一种用法,这回它只出现一个,并且 % 的后边有至少一个空格。

a = 123
b = % a
MsgBox, % a

这样用表示 % 后边的内容按表达式来解析。我们可以认为:

b = %a%
MsgBox, %a%

b = % a
MsgBox, % a

是一样的,实际上后者更强大一些。

a:=1
b:=2
c:=3
; 计算 a + b + c,结果为 6 
MsgBox, % a + b + c 
; 语法错误 
MsgBox, %a + b + c% 
;语法正确,但 + 失去计算功能,变成了字符串的一部分 ; 
MsgBox, %a% + %b% + %c%

如果 % 后边不只是一个变量名,而是一个表达式,那么“用法二”有效,“用法一”无效。

(三)使用“用法一”还是“用法二”

我们发现“用法一”和“用法二”的功能上是有重叠的,那么实际情况应该怎么用呢?这个还是因人而异,尽量前后一致即可。但在某些场景“用法一”更方便,某些情况“用法二”更方便,甚至只能用“用法二”。

“用法一”更方便的场景:

; 变量本身包含双引号等特殊字符
c = "%a% %b%"

; 多行字符串中包含变量
c =
(
xxx
%a%
%b%
)

“用法二”更方便的场景:

; 字符串中的变量居多
MsgBox, % a " " b " " c " " d

只能用“用法二”的场景:

; 需要使用表达式
MsgBox, % a + b - c

另外如果考虑性能的话,“方法一”是要比“方法二”快二分之一到一倍的,但一般情况瓶颈不应该出现在这里,所以也不用过多在乎性能问题,如果真的是因为在很大的循环里使用而产生性能问题,修改一下也是很轻松的事情。

 下面的例子展示了什么时候该使用百分号, 什么时候不该.

; 下面的例子展示了什么时候该使用百分号, 什么时候不该.
Var = Text  ; 赋值一些文本给一个变量(传统的).
Number := 6  ; 赋值一个数字给一个变量(表达式).
Var2 = %Var%  ; 赋值一个变量给另一个(传统的).
Var3 := Var  ; 赋值一个变量给另一个(表达式).
Var4 .= Var  ; 追加一个变量到另一个的末尾(表达式).
Var5 += Number  ; 将变量的值与另一个相加(表达式).
Var5 -= Number  ; 将变量的值减去另一个(表达式).
Var6 := SubStr(Var, 2, 2)  ; 变量在函数中. 这总是一个表达式.
Var7 = %Var% Text  ; 赋值一个变量给另一个变量并带有一些额外的文本(传统的).
Var8 := Var " Text"  ; 赋值一个变量给另一个变量并带有一些额外的文本(表达式).
MsgBox, %Var%  ; 变量在命令中.
StringSplit, Var, Var, x  ; 在命令中的变量, 但是它们作为输入或输出变量.
if (Number = 6)  ; 只要 IF 有括号, 它就是一个表达式, 所以不需要百分号.
if (Var != Number)  ; 只要 IF 有括号, 它就是一个表达式, 所以不需要百分号.
if Number = 6  ; 如果没有括号, 那么 IF 是传统的. 不过, 只有赋值语句 "右边" 的变量需要百分号. 
if Var1 < %Var2%  ; 如果没有括号, 那么 IF 是传统的. 不过, 只有赋值语句 "右边" 的变量需要百分号. 

五、拼接运算符:英文句号

AHK有一个为字符串准备的 . (英文句号) 运算符,它负责以字符串形式拼接两个数据,它的优先级低于普通的算数运算符

如下:

aStr := "喂呀"
Box := 5 * 6 . 666 . "哎哟" . aStr

最终结果得到Box值为:

"30666哎哟喂呀"

在表达式中若只以空格分隔数据,如同. 运算符一般将直接被拼接,如:

aStr := "喂呀"
Box := 5 * 6  666  "哎哟" aStr

与上个示例结果一致

在运用拼接时, 建议把各拼接项目以()括起,没什么特别,可读性嘛。

aStr := "喂呀"
Box := (5 * 6) . (666) . ("哎哟") . (aStr)

同样是结果一致

在拼接项目过多时,可以分行进行字符串元素的拼接,如:

Box := 5 * 6
. 666
. "哎哟"
. aStr

结果一致。

其中首位拼接元素必须置于 := 赋值符号之后,由第2行开始以( . )拼接符号起头。

注意:这样x.y写时,是作为对象访问的运算符。表示 从对象 x 中读取或设置值或调用其方法, 此处 y 是个原义值。


六、取地址运算符&

取址(&): &MyVar 获取 MyVar 的内容的内存地址, 此地址一般和 DllCall 结构一起使用. 同时 &MyVar 也禁用了此变量中的二进制数的缓存, 如果它经常用于数学或数值比较, 那么这会拖慢其性能. 每当变量的地址改变时会重新启用它的缓存(例如使用 VarSetCapacity()).

 

七、没有存在感的逗号运算符

任何命令中的首个逗号可以省略 (除非首个参数为空或以 := 或 = 开始, 或命令单独处于延续片段的顶部). 例如:

MsgBox  This  is ok.
MsgBox,This  is ok.

使用逗号运算符常常比分开写单独的表达式速度更快, 尤其是把一个变量的值赋给另一个变量时(例如: x:=y, a:=b). 当越多的表达式联合成单个表达式时, 性能会持续得到提升; 例如, 把五个或十个简单的表达式联合成单个表达式速度可能提升 35%.

八、位运算符(~、&、^、|、>>、<<)

九、运算符优先级

此处优先级是定义表达式中计算的顺序

赋值运算符是一个最低优先级的运算符,*乘法运算符优先级高于:=赋值运算符。

四则运算加减乘除优先级与小学教育所教授的相同,乘除优先于加减。*为乘法运算符,/为除法。

我们可以用()括号决定运算顺序,让你可以在陌生的算数运算符中不至于在优先级上做太多纠缠。

括号内可以是表达式如:

(1 * (3 + 2))

或单独的数值如

(88)
(变量名)
("字符串来一个")

我们阅读代码时, 面对一个较长的表达式。

可以从()括号内部看起
接着寻找最高优先级的运算符,根据运算符特性求值。
表达式根据运算符优先级依序计算数值,如此顺序往复,直到取得最终值

相同优先级的运算符则从左至右逐对运算, 而赋值运算符比较特别,它从右至左开始运算。

如:

a := 3 + b := 3 + c := 3

得到a(9) b(6) c(3), 在实际编码赋值语句编写时不建议如此写法,请按部就班以可读性为修养操守。

 

 

 

给TA捐赠
共{{data.count}}人
人已捐赠
其他

【基础】 如何在 AHK 下载网络内容到文件或变量

2018-11-10 21:09:29

其他教程

web自动化编程工具(持续更新)

2018-11-10 23:23:29

11 条回复 A文章作者 M管理员
  1. bubble panda

    位运算符为啥要支付才能看啊?

  2. 河许人

    20230201:给位运算增加一个例子

  3. abenzhu

    太棒了,正则表达式解决了模糊匹配的问题.

  4. 101

    谢谢

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索