我有一个序列号列表,需要在宏中循环使用。大多数序列号是连续的,但偶尔也会有一些丢失。例如,我可能需要使用序列号500-510、512-513、516。
有没有办法循环浏览这样的列表?我真的不想把每个数字都写出来,比如:500501502503……因为有时我可能有数百个序列号。
此外,列表会随着每次运行而变化,所以我需要能够向用户询问序列号列表,然后将该列表插入vba宏中。不知道该怎么做。
谢谢。
如果它没有比示例字符串复杂得多,可以引用Range
对象,例如:
Sub Test()
Dim str As String: str = "500-510,512-513,516"
For Each i In Range("A" & Replace(Replace(str, "-", ":A"), ",", ",A"))
Debug.Print i.Row
Next
End Sub
很明显,这种方法有局限性(既有连接表示Range
的字符串的长度限制,也有不通过工作表上的行表示的潜在数字的长度限制。
也许更坚实一点的是:
Sub Test()
Dim str As String: str = "500-510,512-513,516"
For Each el In Split(str, ",")
If InStr(1, el, "-") > 0 Then
For x = Val(el) To Val(Right(el, InStrRev(el, "-") - 1))
Debug.Print x
Next
Else
Debug.Print Val(el)
End If
Next
End Sub
至于您的输入字符串验证;您可以研究Like
运算符或更好的正则表达式。
您需要一个函数,它接受一个字符串(如"500-510,512-513,516"
(,并返回由该表达式表示的数字数组。我还没有完全测试以下内容,但它似乎可以完成任务:
代码
Function ParseNonContiguousRange(rangeExpr As String) As Long()
Dim tokens As Variant, token As Variant
Dim rangeStart As Long, rangeEnd As Long, count As Long, i As Long, index As Long
tokens = Split(rangeExpr, ",")
'First pass: count numbers in range
For Each token In tokens
If InStr(token, "-") Then
rangeStart = CLng(Split(token, "-")(0))
rangeEnd = CLng(Split(token, "-")(1))
count = count + rangeEnd - rangeStart
Else
count = count + 1
End If
Next token
Dim result() As Long
ReDim result(count + 1)
'Second pass: populate range
For Each token In tokens
If InStr(token, "-") Then
rangeStart = CLng(Split(token, "-")(0))
rangeEnd = CLng(Split(token, "-")(1))
For i = rangeStart To rangeEnd
result(index) = i
index = index + 1
Next i
Else
result(index) = CLng(token)
index = index + 1
End If
Next token
ParseNonContiguousRange = result
End Function
Sub TestParseNonContiguousRange()
Dim output() As Long
output = ParseNonContiguousRange("500-510,512-513,516")
For Each i In output
Debug.Print i
Next i
End Sub
输出
500
501
502
503
504
505
506
507
508
509
510
512
513
516
获取不同序列的数字数组
除了JvDv的有效答案外,还有一种将项目分配到基于0的一维数组的替代方法,可用于进一步处理:
Sub GetArrayOfNumbers()
Dim numbers As String: numbers = "500-510,512-513,516"
ReDim tmp(10000) ' provide for enough items in temp array
Dim number
For Each number In Split(numbers, ",") ' check each number or pair of numbers
Dim pair: pair = Split(number & "-" & number, "-")
Dim i As Long, counter As Long
For i = Val(pair(0)) To Val(pair(1))
tmp(counter) = i: counter = counter + 1 ' add number to temporary array
Next
Next number
ReDim Preserve tmp(0 To counter - 1) ' reduce to exact items count
Debug.Print Join(tmp, ",") ' (optional) display in VB Editor's Immediate Window
' ~> 500,501,502,503,504,505,506,507,508,509,510,512,513,516
End Sub
方法提示
为了避免区分单个数字和一个数字范围,我将任何数字标记都更改为一对数字,方法是在其自身重新添加相同的标记(前缀为"-"(,这简化了拆分和最终的赋值循环。
因此,拆分最后一个令牌"516-516"
将允许在单个循环步骤中收集相关的数组项,而附加的附录在实际的数字对中并不重要(因为拆分冗余字符串"500-510-500-510"
也会导致正确的值pair(0)
=500和pair(1)
=510(。