要了解在批处理中分配数据类型的第一件事是批处理不允许分配数据类型。所有批处理变量都是等效的。从本质上讲,保存数字、文本甚至布尔值的变量之间没有区别。但是,设置为数字的变量可以被视为数字,我们将在后面的文章中重点讨论这些数据类型。

在本文中,在描述了所有批处理数据类型之后,我们将学习字符串和字符变量。还将使用子字符串和文本替换的方法进一步探索字符串。布尔值不是由批处理的创建者设计的,但是我们将向您展示如何构建和使用这种有用的数据类型。

常见的数据类型

许多编程语言不仅允许而且要求在为每个变量赋值或以任何方式使用它之前将其声明为特定的数据类型。不同的语言有不同的数据类型,这里列出来一些常用的数据类型列表:

  • 字符 --- 单个字母数字字符

  • 字符串 --- 0到多个字母数字字符

  • 整数 --- 正负整数

  • 浮点数 --- 带小数点的数字

  • 布尔型 --- true或false

不管申明数据类型是好还是坏,批处理变量都没有声明具体的数据类型。当解释器第一次发现一个新的变量名时,它就会无中生有。这种做法确实有很大的灵活性,但它可能是棘手和危险的。一个被使用了十几次的变量的一个拼写错误的实例被解释器认为是一个完全不同的变量,编译器不会捕捉到这个错误。相反,它被视为一个可能什么都解决不了的新变量。

变量可以被赋值为整数,并且可以对其执行算术运算。然后可以为同一变量分配文本并将其视为字符串。这也意味着可能会无意中对包含字符串的变量执行算术,但好处是,在将数字写入控制台或报告时,可以很容易地将其视为字符串,而无需进行任何类型的转换。

尽管您不能分配数据类型,但是您可以创建变量并将它们视为类型之一,但是—我必须强调这一点是每个批处理变量的底层结构实际上只是一些无法描述的内存字节。

字符

一个字符仅仅是一个字节的文本;在批处理的世界中,可以把它看作一个非常短的字符串,因为它被当作任何只有一个字符的字符串一样对待。

字符串

字符串是任意长度的文本,包含字母字符、数字和/或特殊字符。下面的命令将aString变量设置为一个5个单词的字符串:

set aString=Awesome Batch Code Dares Excellence

包括嵌入的空格在内,它的长度总计为35个字符,或者用程序员的话说,就是字节。

许多特殊字符,如美元符号和英磅符号,可以显式地包含在字符串中,但其他字符,如百分号,不能包含在字符串中,因为它们在批处理中有特定的用途。在后面的文章中,我们将讨论转义字符,即如何允许在字符串中包含所有字符,但是现在,要理解解释器在遇到字符串中的感叹号时不会中止,但是您可能看不到预期的结果。例如,赋给这个变量的值的最后一个字符是感叹号:

rem 开启延迟扩展
setlocal EnableDelayedExpansion

set aString=Awesome Batch Code Dares Excellence!
> con echo A String is "%aString%"

下面是echo命令的结果:

A String is "Awesome Batch Code Dares Excellence"

标点符号没有写入控制台,因为它没有包含在字符串变量中。

注意:

正如在前面文章中提到的,我们假设延迟扩展始终是启用的。这个示例就是一个很好的例子,因为如果禁用了延迟展开,那么感叹号就只是另一个字符,而不是用于解析变量的分隔符。该字符将作为值的一部分包含,并将与其余文本一起写入控制台。能够将感叹号视为简单的文本可能是禁用延迟扩展的唯一好处。与延迟扩展提供的功能相比,这个微不足道的优势显得微不足道,这就是我推荐普遍使用延迟扩展的原因。

在后面的文章中,我们将讨论如何将字符串和其他数据类型写入文件,但在这里,我将解释如何构建、连接、子字符串和操作字符串。

构建和连接

前面的示例使用单个set命令将值Awesome Batch Code dare Excellence分配给一个变量。下面的六行执行相同的任务:

set a=Awesome
set b=Batch &
set c=Code
set d=Dares
set e= Excellence
set aString=%a% %b%%c% %d%%e%

在实践中,这种方法对于构建字符串来说非常低效,但是它很好地演示了连接的原理。

由字母表的前五个字母定义的变量分别被设置为一个单词。然后在最后一行,所有五个变量被解析并连接在一起以创建aString。注意结果中的四个嵌入空格。一个来自批处理之后的尾随空格,另一个来自Excellence之前的前导空格,另外两个嵌入在最后一个set命令中。

前面的例子展示了如何通过连接其他字符串来创建字符串,但你也可以在现有字符串上添加其他文本:

set longText=This field contains a brutal run-on sentence and if its prose
set longText=%longText% were to be typed into a single line the reader would
set longText=%longText% be forced to scroll way over to the right to read what
set longText=%longText% you are reading now and then scroll way back to the
set longText=%longText% left after mercifully getting to this period.

在这里,一个字符串被附加了四次额外的文本,以创建一个非常长的字符串。这种方法是我创建长字符串变量的首选方法,但是您也可以使用“延续字符”或插入符号(^)来完成相同的任务。当解释器看到行尾的插入符号时,它会将下一行附加到该行:

set longText=This field contains a brutal run-on sentence and if its prose ^
were to be typed into a single line the reader would be forced to scroll way^
 over to the right to read what you are reading now and then scroll way back ^
to the left after mercifully getting to this period.

在这个例子中,三个插入符号被用来组成一个四行set命令。第一行和第三行在插入符号前面有一个空格,它们的下一行从第一个字节开始,导致单词之间有一个空格。为了演示做同样事情的另一种方法,第二个插入符号紧跟着单词way,下一行在下一个单词之前有一个空格,over。结果是一长串单词,全部由一个空格分隔。

我并不推荐这种使用方式,原因很简单,它会破坏我们的缩进编码风格。我将大多数命令缩进两个或多个空格,如set命令的第一行所示,但是后面一行开头的任何空格都被认为是附加文本的一部分。这实际上意味着这些行必须左对齐。

重要:

我把“延续字符”放在引号里,因为这是一个粗略的简化。插入符号实际上是一个转义字符。

子串

任何称职的语言都会支持检索字符串一部分的子字符串函数,批处理也可以胜任这项任务。对于接下来的几个例子,让我们考虑像之前一样设置aString变量:

set aString=Awesome Batch Code Dares Excellence

子字符串函数需要两个数字,偏移量或起始位置以及所需文本的长度。令人惊讶的是,批处理使用了在现代语言中占主导地位的零偏移(下标从0开始数),而不是20世纪语言中更常见的一偏移。这意味着第一个字节是位置0(不是1),第二个字节是位置1,第100个字节是位置99,以此类推。

子串的语法有点笨拙。这个变量通常用百分号解析,但是在结束的百分号前面有一个冒号、一个波浪号、偏移量、一个逗号,最后是长度。因此,以下语法返回aString变量的前三个字符:

set substring=echo %aString:~0,3%

0的偏移量告诉解释器从第一个字节开始,长度定义为3,导致文本Awe被分配给subString。

下面的代码从字符串的第一个单词some中提取文本:

set subString=%aString:~3,4%

我们需要从第四个字节开始,这是一个零偏移量3。如果您发现零偏移量令人困惑,请将偏移量视为子字符串之前的字节数。更明显的是,长度是4。

下面是用硬编码的to和两个空格组合在一起的两个子字符串:

set phrase=%aString:~15,3% to %aString:~8,5%

第15个字节是Code中的大写C,因此第一个子字符串是单词的剩余三个字节。第八个字节是Batch前面的空格,所以接下来的五个字节包含了整个单词。结果是对原始字符串的重新解释:ode to Batch。

如果没有定义长度,解释器返回字符串的剩余部分。为了演示,下面的子字符串没有长度,前面没有逗号。偏移量对应于35个字节变量中之前的25字节:

set subString=%aString:~25%

结果是subString被分配了字符串Excellence,即原始字符串的最后10个字节。

偏移量为负数

请注意以下示例中的负偏移量。有趣的是,这也将 Excellence 分配给变量:

set subString=%aString:~-10%

负偏移量表示起始位置相对于字符串的末尾,而不是开始,这意味着-10告诉解释器子字符串从字符串末尾开始10个字节。因为没有给出长度,所以它返回文本的剩余部分。只要变量存在值,%aString:~-1%是检查其最后一个字节的简单方法。

这两个命令产生相同的ode子字符串:

set subString=%aString:~15,3%
set subString=%aString:~-20,3%

第一个命令的偏移量是从原始字符串开始的15个字节,而第二个命令通过从35字节变量的末尾开始计算20个字节来找到相同的位置。

长度为负数

负长度的工作原理与此类似。不要把它想象成长度;可以把它看作字符串末尾而不是子字符串中的字节数。例如,下面的代码返回一个去掉第一个和最后一个字节的字符串:

set subString=%aString:~1,-1%

你甚至可以使用负长度的负偏移量。下面提取字符串的倒数第二个字节:

set subString=%aString:~-2,-1%

-2的偏移量告诉解释器从倒数第二个字节开始,-1的长度表示删除最后一个字节。

子字符串实践

在批处理中检索子字符串的一个很好的特性是,如果调用超出字符串长度的子字符串,则返回null。因此,解释器在遇到%aString:~99,1%的35字节字符串时不会崩溃,也不会返回一个空格。相反,它只返回一个空字符串。这是一种方便的确定字符串长度的方法,不用担心编译代码中普遍存在的空指针异常。如果第36个字节等于null(即"%aString:~35,1%" equ ""),但第35个字节有值,则字符串的长度正好是35字节。

但是,此语法仅在对已填充的字符串进行子字符串处理时有效。正如我刚才提到的,如果字符串的长度在1到35个字节之间,%aString:~35,1%解析为空,当然,如果字符串的长度在36个字节或更长,它将解析到第36个字节。但是,如果字符串为空或设置为null,则%aString:~35,1%解析为~35,1或冒号和末尾分隔符之间的所有内容。同样,由于这个问题,当尝试检查空字符串的最后一个字节时,%aString:~-1%解析为~-1,而不是您可能期望的null。

现在您知道了如何从另一个字符串中提取字符串的任何部分,但是在前面的示例中,所有的偏移量和长度都是硬编码的。通常,这两个数字将是变量。在下面的例子中,偏移量和长度被定义为命名的变量,并在第三个命令中使用:

rem 不要忘记开启延迟扩展

set offset=15
set length=3
set subString=!aString:~%offset%,%length%!

包含偏移量和长度的百分号首先将这些变量解析为它们的数值。然后加上感叹号,这样!aString:~15,3!解析为我们熟悉的ode,这是实现延迟扩展的另一个成功。

在后面的文章中,我将讨论算术运算,在完成之后,您将能够计算保存整数值的变量,这些整数值将用作偏移量和长度来查找子字符串。

文本替换

批处理还有一个方便的机制,可以用其他文本替换字符串的全部或部分。例如,假设以下变量包含这个不推荐的文件名:

set filNm=File_Name_With_Underscores.docx

如果不喜欢这个文件名,可以将下划线更改为破折号。在后续文章中,我们也将介绍重命名文件的推荐命令,但这里我将讨论如何构建一个包含新文件名的变量。

文本替换语法类似于用于子字符串的语法。变量和冒号像以前一样被百分号包围,但是现在没有波浪号了。相反,冒号之后是要搜索和更改的文本,后面跟着等号分隔符,最后是替换文本:

set newFilNm=%filNm:_=-%

每个下划线字符(_),而不仅仅是遇到的第一个下划线字符,都被更改为破折号(-),从而产生File-Name-With-Underscores.docx。注意不要更改过多的文本。

看看这个文件名,将“下划线”改为“破折号”也是有意义的。幸运的是,批处理不要求目标文本和替换文本的长度相同,所以这个额外的命令进一步将这个变量的值更新为File-Name-With-Dashes.docx:

set newFileNm=%newFileNm:underscor=Dash%

由于两个单词都以es结尾,所以我使用单数 Dash 作为替换文本,目标文本是 underscor,这不是一个完整的单词。另外,请注意,在变量的值中,Underscores 是大写的,而在替换语法中,underscor 是小写的。非常重要的是,批处理会进行不区分大小写的替换。目标文本可以是任何一种情况,甚至可以是混合情况,对结果没有影响,但是替换文本将完全按照命令中输入的方式使用。因此,%newFilNm:UNDERscor=Dash%在功能上与上一个命令中的变量解析相同,但是%newFilNm:UNDERscor=DASH%将产生一个新的文件名File-Name-With-DASHes.docx

这很奇妙,但前面两个命令显示了两种不同的赋值方法。第一个将修改后的filNm值赋给newFilNm,使filNm保持不变。第二个命令将newFilNm重新分配给它自己,以便它的最终值反映两个文本替换。这两种方法为您提供了灵活性,可以就地更改变量的值,也可以维护两个变量,一个包含旧文本,一个包含新文本。

还可以使用延迟扩展将目标文本(target)和替换文本(repl)转换为变量。这里有一个例子:

set targ=Love
set repl=Hate
set aString=I Love Broccoli
set aString=!aString:%targ%=%repl%!

结果是字符串“I Hate Broccoli”。

文本搜索是文本替换语法的一个很好的应用。在后续文章中,我们将比较和对比确定一个字符串是否是另一个字符串的一部分的两种方法。findstr命令工作得很好,但是基于前面语法的方法执行时间很短。剧透警告:文本搜索逻辑将搜索到的文本替换为null,并将结果与原始文本进行比较。如果它们不同,则找到了文本。

布尔值

布尔值一直存在于编译语言中,有两种,而且只有两种可能的状态:真(true)或假(false)。一旦设置好,您就可以单独使用它们作为if命令中的条件子句,将其计算为true或false,从而确定是否应该执行代码块。批处理并不显式地支持布尔值,但是只要稍微使用一点技巧,您就可以创建它们。

“God.txt存在吗?”在前面的文章中,我展示了如何使用if命令来确定文本文件是否存在:

if exist D:\Batch\God.txt (
   set god=Found
) else (
   set god=NotFound
)

根据文件在某一时刻的状态将变量设置为FoundNotFound。然后可以在将来询问god变量,以确定god .txt在较早的时间是否存在。它可以运行,但有点笨重;布尔值将提供更优雅的解决方案。然后,您可以轻松地在整个代码中多次引用布尔值,甚至可能重置它。

设置和计算布尔值

在批处理中,像所有变量一样,布尔值实际上只是一些文本,但是这些文本可以被看作为true或false。按照惯例,我总是在布尔变量名的前面加上小写b和大写字符,以使其作为布尔值突出。(一个更详细和描述性的选项是以bool文本开头。)让我们复制上一个示例中的逻辑,唯一的区别是变量god被布尔值bGod替换,如果找到God .txt,则将其设置为true,如果未找到则设置为false:

if exist D:\Batch\God.txt (
   set bGox=true==true
) else (
   set bGox=false==x
)

在其他语言中,布尔值被显式设置为true或false。例如,一个有效的Java命令是bGod = true;但是前面的关于批处理中的布尔值的set命令看起来有点不同;特别地,每个都有三个等号。第一个仅仅是用来赋值,另外两个是用来指定值。当 if 命令的条件子句为true时,我们将bGod设置为true==true;如果不是,则false==x为值。这当然看起来很奇怪,但是现在这个变量,虽然从技术上讲仍然只是文本,但可以作为另一个if命令的条件子句进行判断,如下所示:

if %bGod%  > con echo Let us pray.

如果bGod被设置为我们认为的true,解释器将If %bGod%解析为If true == true。变量包含一个相等运算符,双等号,两边的值相同。(不要问操作符周围的空格,但这是解释器看到的。)将所有这些放在 if 命令之后,它的计算结果为true。

但是,如果将变量设置为我们认为的false,则该命令将被解析为If false == x,这将比较两个明显不同的值,从而导致If命令后面的代码不执行。

带布尔值的if命令也可以与not子句一起使用:

if not %bGod%  > con echo Live every day to the fullest.

如果文本(if not %bGod%)解析为If not true == true,则计算结果 not true 或 false。但是当文本解析为双否定if not false == x时,它的计算结果为not false或not true,并将文本写入控制台。

布尔值转换为字符串

我选择true==true作为true的值,但是x==x或0 == 0也可以,并且需要更少的按键。即使false==false也会求值为真,但这并不难。同样,false==x可以包含任意两个不同的字符串,但我选择了这两个值,以便文本true或false在布尔值的前面。布尔变量的结构允许您在编译代码中模拟布尔值的另一个特性——将布尔值转换为字符串。

作为结构化的,您可以将批处理中的布尔值转换为字符串true或false,只需去掉两个等号之后和包含的所有内容。当我们在看到for命令时,我将展示它是如何工作的,但是现在,下面这行代码将截断多余的文本:

for /F "delims==" %%b in ("%bGod%") do  set bStrGod=%%b

在对一个有效的布尔值执行此命令后,名为bStrGod的布尔字符串变量将包含true或false。

如果布尔变量的前缀是b,那么用b作为布尔字符串变量的前缀可能是有意义的。

总结

字符串在批处理中无处不在,在本文中,我们详细介绍了如何构建和连接它们。子字符串和文本替换是所有批处理程序员都应该掌握的两个强大而有用的工具,尽管它们的语法很深奥。布尔值并不是那么普遍,但我希望我展示了这种未充分利用的数据类型的有用性。

在下一篇文章中,我们将继续讨论数据类型,深入研究数值数据类型。我将详细介绍三种不同进制的整数和浮点数,为探索批处理中如何处理算术提供了一个很好的机会。

本文由博客一文多发平台 OpenWrite 发布!