您好,LA。您要知道,这正是脚本专家超越自我的问题之一。(请注意,倒不是说超越脚本专家是特别难的事。)首先,我们是在某个星期五编写本专栏,而且我们始终是在星期五寻求简单的解决方法。其次,恰好几天前我们参与了有关字数统计的讨论,所以我们的脑海里已经存在了这个主题。此问题听起来很简单,而且我们已经开始在考虑字数统计的问题:将上述两点加到一起,您就看到了星期五的完美专栏。
就是说我们的确思考了这个问题。
当我们坐下来想出您的问题的答案时,就立刻出现了第一处小麻烦。毕竟,我们还可以借助其它一些方法解决此问题。例如,通过 Microsoft Word 很容易就可以计算字数,因此我们脑海中浮现的第一个想法就是:“使用 Microsoft Word 就行了。”但是这似乎有些小题大做,而且我们也不想暗示只有出去买个 Microsoft Office 软件才能统计文本文件中的字数。(即使 Office 团队会给我们佣金,我们也要重新考虑这个想法。)然后我们会想到:“您知道,使用常规表达式可能是理想的方案。”不过,只要一想到常规表达式就会令人感到头痛,所以我们也放弃了该想法。
然后,我们想出了这个简单而明确的解决方案:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)
strText = objFile.ReadAll
objFile.Close
arrWords = Split(strText, " ")
Wscript.Echo Ubound(arrWords) + 1
确实简单而明确:我们只需要打开文本文件 C:\Scripts\Test.txt,然后将整个文本文件存储到一个名为 strText 的变量中。然后,使用 Split 函数按照空格拆分数组(因为考虑到只有在两个字之间才会有空格。)使用 Split 函数创建了一个名为 arrWords 的数组(在这样的数组中,每个元素都代表一个单独的字)之后,我们仅需回显数组的 Ubound(上限)值,并加上 1。(为何要加上 1 呢?因为数组的 Ubound 值始终为数组中的项数减 1。)
这在某种程度上是可行的。可结果是,我们使用的文本文件有时会产生额外的空格以对齐信息:
Name Date
Ken Myer 3/30/2006
Pilar Ackerman 3/31/2006
这就会带来一个问题:其中每一个额外空格都作为一个字计数。这样,我们最终得出的字数就要比实际字数稍多一些。
让我们从头开始:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)
strText = objFile.ReadAll
objFile.Close
arrWords = Split(strText, " ")
For Each strWord in arrWords
If Len(strWord) > 0 Then
i = i + 1
End If
Next
Wscript.Echo i
正如您所见到的,这次我们没有回显 Ubound 值。而是设置了一个 For Each 循环,以遍历数组中的所有项目。在该循环中,我们使用 Len 函数来确定每个单项中的字符数。如果项目的长度为 0,则说明我们遇到了一个多余空格。在这种情况下,我们仅需跳过该项目即可(因为几乎没有字会包含 0 个字符)。如果长度大于 0,则我们以 1 为单位递增计数器变量的值:
i = i + 1
遍历整个数组后,我们再回显计数器变量值:
Wscript.Echo i
这次准确多了,但是字数值似乎还是有点儿高。在对此问题苦思了一两分钟后,我们发现了原因。假定我们的文本文件包括这个句子:
Two plus two = four
多数人会说这个句子中有 4 个字;但我们的脚本却坚持认为此句中有 5 个字:
Two
plus
two
=
four.
为什么说有 5 个字呢?因为该脚本将等号 (=) 也作为一个字计入在内。同样,我们在文档中还有其他“无关的”字符:例如,以下结构会自动作为 3 个字计数:
. . .
真是让人厌烦。
我们不喜欢这样计数,因此我们最后一次修改了该脚本,用一系列的 Replace 函数来替换像等号和包含空格的句点这样的字符:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\scripts\test.txt", ForReading)
strText = objFile.ReadAll
strText = Replace(strText, ",", " ")
strText = Replace(strText, ".", " ")
strText = Replace(strText, "!", " ")
strText = Replace(strText, "?", " ")
strText = Replace(strText, ">", " ")
strText = Replace(strText, "<", " ")
strText = Replace(strText, "&", " ")
strText = Replace(strText, "*", " ")
strText = Replace(strText, "=", " ")
strText = Replace(strText, vbCrLf, " ")
objFile.Close
arrWords = Split(strText, " ")
For Each strWord in arrWords
If Len(strWord) > 0 Then
i = i + 1
End If
Next
Wscript.Echo i
我们更钟意这个脚本。与我们先前的脚本一样,我们首先定义一个名为 ForReading 的常量;此常量会告知 FileSystemObject 我们要读取该文本文件(而不是向该文件写入或添加数据)。接下来我们创建一个 FileSystemObject 实例并使用 OpenTextFile 方法打开文件 C:\Scripts\Test.txt。一旦调出并运行 FileSystemObject 后,我们就使用 ReadAll 方法将该文件的全部内容读取到名为 strText 的变量中:
strText = objFile.ReadAll
然后,我们执行一系列的 Replace 函数以替换变量 strText 中的字符。(请注意,我们不会处理实际文件其本身,而只是处理内存中存储的该文件的副本。)例如,本行代码会以空格替换 strText 中的所有逗号:
strText = Replace(strText, ",", " ")
即便有要替换的字符,也请您自己决定替换哪些字符。如果您确定将等号和加号 (+) 作为单个字计数,则根本不必进行任何替换。
等一下,还要检查一个问题:有一个您必须要执行的替换。假定我们有如下所示的文本文件:
A
B
C
D
E
在这个文本文件中有多少个字呢?我们也会说有 5 个字,但是该脚本却认为仅有 1 个字。为什么?好吧,我们指示该脚本在空格处拆分文本;然而,在本文件中却没有任何空格,只是在每行的结尾处有回车换行符而已。因此,在我们的数组中仅包含一项。哎。
那么,我们如何解决这个问题呢?实际上,这相当容易:我们只要用空格替换所有回车换行符 (vbCrLf) 就行了:
strText = Replace(strText, vbCrLf, " ")
只要我们在每个字符之间用空格分隔(而不是用回车换行符分隔),该脚本就会针对此示例文本文件正确返回字数 5。
好了,我们讲到哪了?哦,是的。关闭文件后,我们则调用 Split 函数以将 strText 拆分为一个数组。然后,我们使用前述的 For Each 循环来统计数组中的字数(由此来统计该文本文件中的字数),遇到多余的空格则跳过。然后,我们回显计数器变量的值就万事大吉了。
至少我们对这个结果很满意。该字数是否 100% 准确还带有一些主观性。例如,假定文本文件中包含以下行:
2+2=4
在此行中有 5 个字(2、+、2、= 和 4)吗?也许仅有 3 个字:2、2 和 4。也许仅有 1 个字:2+2=4。(Microsoft Word 将其视为一个单字。)对此,您必须自己决定。对我们而言,我们已决定下次会找一个“容易的”问题来回答,我们会直接越过该问题而去尝试解决其它一些问题!
如何比较两个字符串值而不考虑大小写呢?
问:
嗨,脚本专家!我拥有一个可对两个电子邮件地址进行比较并可告诉我它们是否相同的脚本。有时电子邮件地址是相同的,而比较结果却并非如此:例如,其中一个地址可能是 example@abc.com,而另一个可能是 example@ABC.com。我的脚本始终会告诉我它们是不同的电子邮件地址。如何解决此问题?
-- PS
答:
您好,PS。您知道,如果人们了解了脚本专家到底是干什么的,他们将从来也不会向我们提出类似的脚本编写问题。例如,大约一周前,一个脚本专家正要准备去全家渡假。在订票时他需要向售票代理处说出他妹妹的婚后姓名。“K……”他说道,然后就卡在那了。对于他的生活而言,这位脚本专家想不起一个以字母 K 开头的单词。
那么,这与您的问题又有什么关系呢?毫无关系。我们只是认为这是一个有趣的轶事。
好了,但您的问题又如何呢?如您所知,我们经常告诉人们,编写脚本时请不要担心大小写问题。“总体而言,VBScript 不区分大小写,”我们这样告诉人们。“ABC 与 abc 相同。”
而且有例为证,构成 VBScript 的关键字、函数、语句以及其他特色内容通常均不区分大小写。例如,下面这行代码(虽然其看起来有点怪)将弹出一个消息框,这一点毫无疑问:
mSGboX "This is a message box."
然而,仅仅因为您可按您所希望使用的任何方式键入 Msgbox 并不意味着 VBScript 执行的过程和测试也是不区分大小写的。例如,请尝试运行以下脚本,该脚本可比较 example@abc.com 和 example@ABC.com:
str1 = "example@abc.com"
str2 = "example@ABC.com"
If str1 = str2 Then
Wscript.Echo "The strings are equal."
Else
Wscript.Echo "The strings are not equal."
End If
运行该脚本,之后您将收到以下消息:
The strings are not equal.
为什么是这样呢?是这样,默认情况下,当 VBScript 比较字符串值时,将比较字符串中各个字符的 ASCII 值。在神奇的 ASCII 世界里,大写字母 A 和小写字母 a 具有不同的值(它们的值分别是 65 和 97)。由于 ASCII 值不同,因此 VBScript 会认为这两个字符串是不同的。
那么我们如何解决此问题呢?我们将为您提供两种不同的解决方案。
对于初学者,您可使用 VBScript 函数 StrComp(用于比较字符串);这将有助于确保对您的脚本进行文本比较而不是进行二进制比较。(二进制比较会将 A 和 a 视为不同的字符;而文本比较则不然。)例如:
str1 = "example@abc.com"
str2 = "example@ABC.com"
intCompare = StrComp(str1, str2, vbTextCompare)
If intCompare = 0 Then
Wscript.Echo "The strings are equal."
Else
Wscript.Echo "The strings are not equal."
End If
在此脚本中,我们指定了两个字符串值(str1 和 str2),然后调用 StrComp 函数。StrComp 将得到三个参数:两个要进行比较的字符串和 VBScript 常量 vbTextCompare。随后会将字符串比较的结果存储到被称为 intCompare 的变量中。如果 intCompare 等于 0,则两个字符串相等;如果 intCompare 不等于 0,则两个字符串不同。
请试一试,看看会发生什么。您应该会收到以下消息:
The strings are equal.
哇。这要好办多了。
下面将介绍另外一种方法,可确保比较字符串时不考虑字母大小写。在此脚本中,我们使用 UCase 函数将这两个字符串中的所有字母都转换为大写字母(换言之,两个字符串都将被转换为 EXAMPLE@ABC.COM)。由于不存在可担心的小写字母,因此此脚本将报告这两个字符串是相同的:
str1 = UCase("example@abc.com")
str2 = UCase("example@ABC.com")
If str1 = str2 Then
Wscript.Echo "The strings are equal."
Else
Wscript.Echo "The strings are not equal."
End If
请记住,只有不考虑大小写时,才应使用其中的一种方法。如果要考虑大小写(即,如果不应将 example@ABC.com 和 example@abc.com 视为相同),则只需使用原来的常规等价测试:
str1 = "example@abc.com"
str2 = "example@ABC.com"
If str1 = str2 Then
Wscript.Echo "The strings are equal."
Else
Wscript.Echo "The strings are not equal."
End If
如果您想知道结果如何,结果就是:我们的这位脚本专家最后终于想出了一个以字母 K 开头的单词“Kansas”。非常好!