I'm fairly new to scripting and am in need of some help. I have come across a unique situation for a Non-Profit client of ours that requires us to compare two or more files in a specific folder and move the file with the lowest numerical value in the filename.
This organization runs a non-profit radio station which has content submitted from hundreds of volunteers that name their files (when they record more than one) with various numbers at the end that either represent the date or the order in which the files are to be aired.
Essentially I am looking to create a vbscript (because I think it can be done this way) that will run with windows task scheduler 30 minutes prior to the first air date of the content and move the file with the lowest value (if more than one file exists) to a folder where it will be automatically processed by the radio automation software.
Examples of files in a folder might look something like these:
Folder1: (in this instance, "news.mp3" is the lowest value)
news.mp3
news1.mp3
news2.mp3
Folder2:
entertainment24.mp3
entertainment26.mp3
Folder3:
localnews081420.mp3
localnews081520.mp3
Honestly, on this one, I'm not even sure where to start. I've found several scripts that can look at file date or a specific numerical or date format in the filename, but none that can parse numbers from a filename and move/copy a file based on the numerical value. I'm hoping there is someone out there smarter than me that can point me in the right direction. Thanks for looking at my problem!
One script I've been playing with (from the scripting guy) looks at specific years in a filename:
strComputer = “.”
Set objWMIService = GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)
Set colFiles = objWMIService.ExecQuery _
(“ASSOCIATORS OF {Win32_Directory.Name=’C:\Test’} Where ” _
& “ResultClass = CIM_DataFile”)
Set objRegEx = CreateObject(“VBScript.RegExp”)
For Each objFile in colFiles
objRegEx.Global = True
objRegEx.Pattern = “\d{4}”
strSearchString = objFile.FileName
Set colMatches = objRegEx.Execute(strSearchString)
strYear = colMatches(0).Value
strNewFile = “C:\Test\” & strYear & “\” & objFile.FileName & _
“.” & objFile.Extension
objFile.Copy(strNewFile)
objFile.Delete
Next
...but I can't seem to make the leap to regular numbers and then take a lowest value...
You can use FileSystemObject to Work with Drives, Folders and Files.
Also i used GETNUM function to get number.
Try my way :
sFolder = "C:\Test\"
Set oFSO = CreateObject("Scripting.FileSystemObject")
For Each objFile in oFSO.GetFolder(sFolder).Files
Number=GETNUM(objFile.Name)
strNewFile = sFolder & Number & "\" & objFile.Name
If NOT (oFSO.FolderExists(sFolder & Number)) Then
oFSO.CreateFolder(sFolder & Number)
End If
oFSO.MoveFile objFile, strNewFile
Next
Function GETNUM(Str)
For i=1 To Len(Str)
if IsNumeric(Mid(Str,i,1)) Then
Num=Num&Mid(Str,i,1)
End if
Next
GETNUM=Num
End Function
For understanding the used code and how they work, open these sites and read all pages very carefully.
MoveFile method
Vbs Script to check if a folder exist
Introduction
I already have a working solution to the problem this question will describe. Being new to programming, I think my solution is fairly ugly, but: 1) I am not sure that’s true, and 2) I don’t see a more elegant solution after a solid week reading around the internet studying. Since I can easily solve the problem on an ad hoc basis using Pivot Tables and the subject spreadsheet is common report style, I am thinking that many others have already solved this problem (but I haven’t found it or know the keywords to search for). I will provide the raw data, the desired output, the code I currently have working, and some alternatives that I’ve looked at, but didn’t use and why. I am asking you for a better programming approach -- if you see one -- or at least validation that my method was reasonably as efficient as it could be done. Thank you.
The Raw Data
Below is an example of a report which contains data to be summarized:
Report #| Assignee| Type of Report| Department| Status
1 | Shannon| Ad hoc| Accounting| Declined
2 | Shannon| Ad hoc| Accounting| Completed
3 | Shannon| Change| Accounting| New
4 | Shannon| Change| Shipping| In Progress
5 | Shannon| Training| Shipping| Declined
6 | Shannon| Training| CustService| Completed
7 | Shannon| Training| CustService| New
8 | Jason| Ad hoc| CustService| In Progress
9 | Jason| Change| CustService| Declined
10 | Jason| Ad hoc| Accounting| Completed
11 | Jason| Training| Accounting| New
12 | Thomas| Training| Accounting| In Progress
13 | Jason| Change| Shipping| Declined
14 | Jason| Ad hoc| Shipping| Completed
15 | Thomas| Ad hoc| CustService| New
16 | Jason| New| CustService| In Progress
17 | Thomas| New| CustService| Declined
18 | Thomas| Change| CustService| Completed
19 | Thomas| Ad hoc| Shipping| New
20 | Thomas| Change| Shipping| In Progress
-Continues in similar fashion until:-
545 | Phyllis| New| Accounting| Declined
Desired Output
The data needs to be summarized in a manner similar to what is below. That is, it is filtered by Assignee name, and a count of the subcategories is the output for each parent category. (Note: This output could be easily obtained using a Pivot Table ad hoc, but I want to put it into a running table that builds over time programmatically for trend analysis.)
Shannon: Type of Report Department Status
Ad hoc= 25 Accounting= 45 Declined = 12
Change= 13 CustService= 2 In Progress= 24
Training= 3 Shipping= 75 New= 56
New= 81 Completed= 30
Jason: Type of Report Department Status
Ad hoc= 12 Accounting= 21 Declined = 0
Change= 3 CustService= 23 In Progress= 12
Training= 20 Shipping= 4 New= 12
New= 13 Completed= 24
-Continues for each "Assignee"-
The summarizing I am doing is very similar to what you get from a PivotTable, and that's currently how the manual process gets the data. I need to get the data, however, and put into a daily tracking table which retains the historical counts and is used for trend analysis on each assignee, so a PivotTable by itself isn’t a solution.
Currently Working Code
This is the entire piece of working code, including two subs that are called which are appended to the end:
Sub CollateData()
Dim HdrNm As New Collection 'Collection used to read and reference column indices.
'Variables used for referencing the "Assigned To" column
Dim Assignee As New Scripting.Dictionary
Dim nmAssignee As New Scripting.Dictionary
Dim Asgn As String
Dim a As Integer
Dim aKey As Variant
'Variables used for referencing the "Type of Report" column
Dim TypRep As New Scripting.Dictionary
Dim nmTypRep As New Scripting.Dictionary
Dim arrTypRep() As Integer
Dim Typ As String
Dim t As Integer
'Variables used for referencing the "Department" column
Dim Dept As New Scripting.Dictionary
Dim nmDept As New Scripting.Dictionary
Dim arrDept() As Integer
Dim Bus As String
Dim b As Integer
'Variables used for referencing the "Task Status" column
Dim TskStatus As New Scripting.Dictionary
Dim nmTskStatus As New Scripting.Dictionary
Dim arrTskStatus() As Integer
Dim Tsk As String
Dim s As Integer
'Other variables
Dim DataWS As Worksheet
Dim ScratchWS As Worksheet
Dim lastrow As Integer, x As Integer
Set DataWS = ThisWorkbook.Worksheets("SheetWithRawData")
lastrow = DataWS.Cells(Rows.Count, 11).End(xlUp).Row
Call ReadHeaderRow(DataWS, HdrNm) 'Fills the HdrNm collection with column index using column headers for keys
'Initialize variables for the loop that follows
a = 1
t = 1
b = 1
s = 1
'This next seciont/first loop goes through the report to identify a unique list of assignees and category lists _
' which need to be summed. These lists will be used to ReDim the 2-dimensional arrays to appropriate _
' size, as well as reference the elements of the 2D array(s).
'
' NOTE: I am using the seemingly duplicative Dictionaries (e.g. TypRep & nmTypeRep) in order to have _
' access to the category as both a string and as an integer/index.
For x = 2 To lastrow
If Not Assignee.Exists(DataWS.Cells(x, HdrNm("Assigned to")).Value) Then
Assignee.Add DataWS.Cells(x, HdrNm("Assigned to")).Value, a
nmAssignee.Add a, DataWS.Cells(x, HdrNm("Assigned to")).Value
a = a + 1
End If
If Not TypRep.Exists(DataWS.Cells(x, HdrNm("Type of Report")).Value) Then
TypRep.Add DataWS.Cells(x, HdrNm("Type of Report")).Value, t
nmTypRep.Add t, DataWS.Cells(x, HdrNm("Type of Report")).Value
t = t + 1
End If
If Not Dept.Exists(DataWS.Cells(x, HdrNm("Department")).Value) Then
Dept.Add DataWS.Cells(x, HdrNm("Department")).Value, b
nmDept.Add b, DataWS.Cells(x, HdrNm("Department")).Value
b = b + 1
End If
If Not TskStatus.Exists(DataWS.Cells(x, HdrNm("Task Status")).Value) Then
TskStatus.Add DataWS.Cells(x, HdrNm("Task Status")).Value, s
nmTskStatus.Add s, DataWS.Cells(x, HdrNm("Task Status")).Value
s = s + 1
End If
Next x
'Assign the appropriate dimensions to the following 2D arrays
ReDim arrTypRep(1 To Assignee.Count, 1 To TypRep.Count)
ReDim arrDept(1 To Assignee.Count, 1 To Dept.Count)
ReDim arrTskStatus(1 To Assignee.Count, 1 To TskStatus.Count)
'The following, second loop now goes through and sums up the count of each category element for each _
' Assignee. Using this technique, I only go through the list/report once (or twice, if you consider _
' the previous loop to dimension the arrays) in order to tabulate the desired data.
For x = 2 To lastrow
Asgn = DataWS.Cells(x, HdrNm("Assigned to")).Value
Typ = DataWS.Cells(x, HdrNm("Type of Report")).Value
Bus = DataWS.Cells(x, HdrNm("Department")).Value
Tsk = DataWS.Cells(x, HdrNm("Task Status")).Value
arrTypRep(Assignee.item(Asgn), TypRep.item(Typ)) = arrTypRep(Assignee.item(Asgn), TypRep.item(Typ)) + 1
arrDept(Assignee.item(Asgn), Dept.item(Bus)) = arrDept(Assignee.item(Asgn), Dept.item(Bus)) + 1
arrTskStatus(Assignee.item(Asgn), TskStatus.item(Tsk)) = arrTskStatus(Assignee.item(Asgn), TskStatus.item(Tsk)) + 1
Next x
'Now to generate the output of the data we collected:
On Error Resume Next
Application.DisplayAlerts = False
With ThisWorkbook
.Worksheets("DesiredOutput").Delete
.Worksheets.Add after:=.Worksheets(1)
End With
Application.DisplayAlerts = True
On Error GoTo 0
Set ScratchWS = ThisWorkbook.ActiveSheet
ScratchWS.Name = "DesiredOutput"
x = 1
'Loop through each Assignee and dump out the collected counts
For Each aKey In Assignee
Call OutputData("Type of Report", Assignee, nmAssignee, aKey, TypRep, nmTypRep, arrTypRep, x)
Call OutputData("Department", Assignee, nmAssignee, aKey, Dept, nmDept, arrDept, x)
Call OutputData("Task Status", Assignee, nmAssignee, aKey, TskStatus, nmTskStatus, arrTskStatus, x)
Next aKey
Range("B1").ColumnWidth = 3
Range("A1, C1").EntireColumn.AutoFit
End Sub
****************************************************************************
****************************************************************************
Sub OutputData(Title As String, Assignee As Scripting.Dictionary, nmAssignee As Scripting.Dictionary, _
aKey As Variant, ReportCategory As Scripting.Dictionary, nmReportCategory As Scripting.Dictionary, _
arrCategory() As Integer, x As Integer)
Dim CatKey As Variant
With Cells(x, 2)
.Value = Title
.Font.Bold = True
End With
x = x + 1
For Each CatKey In ReportCategory
Cells(x, 1).Value = nmAssignee.item(Assignee.item(aKey))
Cells(x, 3).Value = nmReportCategory.item(ReportCategory.item(CatKey))
Cells(x, 4).Value = arrCategory(Assignee.item(aKey), ReportCategory.item(CatKey))
x = x + 1
Next CatKey
x = x + 1
End Sub
**************************************************************************
**************************************************************************
Private Sub ReadHeaderRow(TargetWS As Worksheet, HdrNm As Collection)
Dim lastcolumn As Integer
Dim x As Integer
lastcolumn = TargetWS.Cells(1, Columns.Count).End(xlToLeft).Column
For x = 1 To lastcolumn
HdrNm.Add TargetWS.Cells(1, x).Column, TargetWS.Cells(1, x).Value
Next x
End Sub
Other Possible Solutions Considered/Discarded
Looping through list for each Assignee
I thought about generating a dictionary/collection of assignees and then looping through the report gathering the data for each assignee on each pass, but the number of assignees could change (go up), and the report list could go up, and thus many extraneous passes through the list.
Jagged Arrays/Collections/Dictionaries
I was excited when I first learned that I could do a dictionary of dictionaries (array of arrays, etc), but as I understand it, I can't use the first dictionary for assignees (1st Dimension) and a second dictionary for all of one category (e.g. Type of Report) as the second dimension. I would actually need to create a separate dictionary for each assignee and category. In other words, if I had just one category (Type of Report) and 15 assignees, I would actually need to create 16 dictionaries: 1st dictionary would be "Assignee" --the assignee's name as the key, and 2nd through 16th dictionaries (TypRep1 through TypRep15) as the items corresponding to the keys in dictionary Assignee. Plus, I cannot create the dictionaries dynamically as the number of Assignees might change, so this method was out for me, unless I've misunderstood something important (always possible). My knowledge of jagged datatypes comes from here: http://bytecomb.com/collections-of-collections-in-vba/
Custom Data Type
I haven't tried this because I just came across it, and I don't know much about it, but maybe this problem would be solved as a custom data type. I'm going to go read about them more, but perhaps that's a better solution in a way I do not yet understand.
Concluding Statement
I know this was a lot to read, sorry. Thanks for sticking this far. I would greatly appreciate any suggestions on how to achieve what my code above does achieve. I feel confident that the reason I haven't found a better way is that this problem's solution is obvious to everyone but me, and it seems like it would be something anyone coding in VBA/Excel would come across with some frequency. Thank you for your helpful input.
We use objects in programming. Most computers have Excel, so you could get excel to do it for you.
Windows come with an object, which I think of as a datatype. A created in memory, disconnected recordset.
So you would do
rs.filter = "Assignee='Shannon' AND Status='Cancelled'"
then
msgbox rs.recordcount
Gives you the number.
Or you can sort and enumerate.
This cuts lines from the top or bottom of a file.
cscript scriptname.vbs "" t x 5 <infile.txt >outfile.txt
Set Arg = WScript.Arguments
set WshShell = createObject("Wscript.Shell")
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
Set rs = CreateObject("ADODB.Recordset")
With rs
.Fields.Append "LineNumber", 4
.Fields.Append "Txt", 201, 5000
.Open
LineCount = 0
Do Until Inp.AtEndOfStream
LineCount = LineCount + 1
.AddNew
.Fields("LineNumber").value = LineCount
.Fields("Txt").value = Inp.readline
.UpDate
Loop
.Sort = "LineNumber ASC"
If LCase(Arg(1)) = "t" then
If LCase(Arg(2)) = "i" then
.filter = "LineNumber < " & LCase(Arg(3)) + 1
ElseIf LCase(Arg(2)) = "x" then
.filter = "LineNumber > " & LCase(Arg(3))
End If
ElseIf LCase(Arg(1)) = "b" then
If LCase(Arg(2)) = "i" then
.filter = "LineNumber > " & LineCount - LCase(Arg(3))
ElseIf LCase(Arg(2)) = "x" then
.filter = "LineNumber < " & LineCount - LCase(Arg(3)) + 1
End If
End If
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
One approach for you would be to put your 4 criteria into 4 arrays- Assignee, Status, Dept, theotherone.
For each a in assignee()
For each b in Status()
For each c in Dept()
For each d in other()
.filter = "assinnee=" & a & "AND Status=" & b & "And dept=" & c
msgbox .recordcount
Next
Next
Next
Next
I am new to vb express and looking for a way to read two lines in a text file get the difference between then and loop it till the end its a simple clock in clock out system which store each persons clock on and off time in a text file like so
03/11/2014 09:55:02
03/11/2014 14:55:02
03/11/2014 16:55:02
03/11/2014 19:55:02
04/11/2014 09:00:02
04/11/2014 13:00:00
I know I use the DateDiff to get the time but I only want them to work out the difference between line 1 and 2 then 3 and 4 and add them all up is it possible to do that without over complicating things?
I guys I have worked it out I have done this by reading the text filed line by line in a loop at the moment I have not put any validation in to show people who have forgot but the basics are there
Dim FILE_NAME As String = "times\08.txt"
Dim start As DateTime
Dim finish As DateTime
Dim total
If System.IO.File.Exists(FILE_NAME) = True Then
Dim objReader As New System.IO.StreamReader(FILE_NAME)
Do While objReader.Peek() <> -1
start = objReader.ReadLine() & vbNewLine
finish = objReader.ReadLine() & vbNewLine
duration = DateDiff(DateInterval.Minute, start, finish)
total = duration + total
Loop
Label2.Text = total
I'm trying to find a way to enhance the reliability of my script. It already works but can be thrown off with a simple extra space in the imported text file.
So I'd like to change my script to Readline if I can find a way to do something like:
Example of text in the .txt file:
FLIGHTS OVER TUSKY PLEASE FILE:
AT OR WEST OF A LINE RBV..LLUND..BAYYS..PUT..DIRECT
FLIGHTS OVER EBONY PLEASE FILE:
AT OR WEST OF A LINE RBV..LLUND..BAYYS..PUT..DIRECT
I know the following doesn't work but if there was a simple modification this would be good.
set WshShell = WScript.CreateObject("WScript.Shell")
Return = WshShell.Run("C:\Downloads\software\putty.exe -load "testing")
set objFSO = CreateObject("Scripting.FileSystemObject")
set objFile = objFSO.OpenTextFile("C:\Users\AW\Desktop\Entries1.txt")
strLine = objFile.ReadAll
If InStr(strLine1, "OVER TUSKY PLEASE") and InStr(strLine2, "BAYYS..PUT..DIRECT") Then
trans307="TUSKY"
ind306="4"
WHAT I'M USING NOW:
I edit the text file in notepad++ to FIND & REPLACE "\n" with "" and "\r" with " " and then it's all one text string and I search for strings within that string.
If InStr(strLine, "FLIGHTS OVER TUSKY PLEASE FILE: AT OR WEST OF A LINE ..RBV..LLUND..BAYYS..PUT..DIRECT") _
or InStr(strLine, "FLIGHTS OVER TUSKY PLEASE FILE: AT OR WEST OF A LINE RBV..LLUND..BAYYS..PUT...DIRECT") Then
trans308C="TUSKY"
ind308C="4"
Problem: If the creators of the text file put another space " " anywhere in this line "AT OR WEST OF A LINE RBV..LLUND..BAYYS..PUT..DIRECT" the script will not identify the string. In the above example I have had to create another or InStr(strLine, "") statement with an extra space or with a couple of dots.
UPDATE:
I will try something like:
set objFSO = CreateObject("Scripting.FileSystemObject")
set objFile = objFSO.OpenTextFile("C:\Users\AW\Desktop\Entries1.txt")
strLine1 = objFile.Readline(1)
strLine2 = objFile.Readline(2)
If InStr(strLine1, "FLIGHTS OVER TUSKY") and InStr(strLine2, "RBV..LLUND..BAYYS..PUT..DIRECT") Then
trans1="TUSKY"
ind1="4"
and see if I can get that to read 2 lines at a time, and loop through the text file.
If you're scared of regex and looking for an alternative, you could create a clunky function to add to your script. Based on your samples, it would seem that fullstops are also never normally used for normal purposes and tend to represent spaces. (I would recommend using Regex instead!)
Using these presumptions, you could create a clunky function like this, that looks for fullstops, and converts them to spaces, removing extra spaces.. Obviously, this relies heavily on your input source files not changing too much - you really should be using a regex to work this stuff out properly.
You could test for the basic expected results using something like the function below.
For example say you had a line of text set in firLine with multiple spaces or fullstops, the function would recognize this:
firLine = "THIS.IS.A.TEST..YOU...SEE MULTIPLE SPACES"
if instr(sanitize(firLine),"THIS IS A TEST YOU SEE MULTIPLE SPACES") then
wscript.echo "Found it"
End If
Here's the clunky function that you could just paste at the end of your script:
Function sanitize(srStr)
Dim preSanitize, srC, spaceMarker
preSanitize = ""
for srC = 1 to len(srStr)
if mid(srStr, srC, 1) = "." then
preSanitize = preSanitize & " "
else
preSanitize = preSanitize & mid(srStr, srC, 1)
End If
spaceMarker = false
sanitize = ""
for srC = 1 to len(preSanitize)
If mid(preSanitize, srC, 1) = " " then
if spaceMarker = false then
sanitize = sanitize & mid(preSanitize, srC, 1)
spaceMarker = true
End If
else
sanitize = sanitize & mid(preSanitize, srC, 1)
spaceMarker = false
End If
Next
End Function
InStr() is a good tool for checking whether a strings contains a fixed/literal string or not. To allow for variation, you should use Regular Expressions (see this or that).
First of all, however, you should work on your specs. Describe in plain words and with some samples what you consider (not) to be a match.
E.g.: A string containing the words "FLIGHTS", "OVER", and "TUSKY" in that order with at least one space in between is a match - "FLIGHTS OVER TUSKY", "FLIGHTS OVER TUSKY"; "FLIGHTS OVER TUSKANY" is a 'near miss' - what about "AIRFLIGHTS OVER TUSKY"?
GREAT NEWS! I finally figured out how to do this.
Here is a snippet from "Entries1.txt"
FLIGHTS OVER BRADD KANNI PLEASE FILE:
VIA J174.RIFLE..ACK..DIRECT
OR RBV.J62.ACK..DIRECT
FLIGHTS OVER KANNI WHALE PLEASE FILE:
VIA J174.RIFLE..ACK..DIRECT OR
FLIGHTS OVER WHALE PLEASE FILE:"
ETC, ETC
set WshShell = WScript.CreateObject("WScript.Shell")
set objFSO = CreateObject("Scripting.FileSystemObject")
set objFile = objFSO.OpenTextFile("C:\Users\AW\Desktop\Entries1.txt")
Do until objFile.AtEndOfStream
firLine = objFile.ReadLine
If InStr(firLine, "FLIGHTS OVER KANNI WHALE PLEASE") Then
secLine = objFile.ReadLine
If InStr(secLine, "J174.RIFLE..ACK..DIRECT") Then
'I'm going to change the below once I piece it all together.
WScript.Echo "works"
Else WScript.Echo "Not found"
'cut, paste and modify all my "IF" statements below
End If
End If
loop
I have a vbscript file that is reading a file and sending each line to a terminal program. When it comes to a semicolon in the middle of the string, it splits the semicolon at the string.
I have been using this code for quite sometime with other strings with no problems. There is one string per line in the file the script is reading.
The string in the file that is causing the problem is: 2101;99PSP
Here is the code I am using (with a terminal emulation program called Reflections):
Sub NarcoticOrderableItemTurnOff()
''# Constants used by OpenTextFile()
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
Const ICON_INFO = 64 ''# Information message; displays 'i' icon.
Set wshshell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = _
objFSO.OpenTextFile("P:\NarcoticOrderableItems.txt", ForReading)
Session.Transmit "^Orderable Item Edit (CPRS)" & vbCr
Do Until objTextFile.AtEndOfStream
strNextLine = objTextFile.ReadLine
arrC2oderableItemList = Split(strNextLine, ";", 3)
'arrServiceList(0) = Area of Use
'arrServiceList(2) = Printer for that area of use
With Session
.WaitForString "Select ORDERABLE ITEMS NAME:"
.Transmit arrC2oderableItemList(0) & vbCr
.WaitForString "//"
.Transmit "N" & vbCr
.WaitForString "//"
.Transmit vbCr
.WaitForString "//"
.Transmit vbCr
.WaitForString "//"
.Transmit vbCr
End With
Loop
objTextFile.close
Session.MsgBox "All done! C2 Orderable Items turned off!", vbExclamation
''#ErrorHandler:
''# Session.MsgBox Err.Description, vbExclamation + vbOKOnly
End Sub
I think it might have something with the following row of code to do:
arrC2oderableItemList = Split(strNextLine, ";", 3)
If this problem string is the whole line from the file you're reading:
2101;99PSP
The problem is that you're trying to get 3 items from every line and this one only has 2. To account for lines that don't have a 3rd item you should remove the 3rd parameter from your Split function and then check the UBound of the Array before using the 3rd item.
arrC2oderableItemList = Split(strNextLine, ";")
If UBound(arrC2oderableItemList) >= 2 Then
''# There are 3 items or more in the Array (O-based)
''# Can do something with arrC2oderableItemList(2)
Else
''# There are only 2 items (or less) in the Array
''# Do not use arrC2oderableItemList(2)
End If
If you are splitting the lines at semicolons but the text contains extra semicolons, you would have to
search for extra semicolons
change them to a pattern that would not normally be found in the text
split your line
change the pattern from step 2 back to semicolons
Or take the easy route and don't allow extra semicolons in the files your reading.
Posting a few example lines (including the problem line) would allow somebody to help you in writing code to search for the extra semicolons.
If (2101;99PSP) is all that is on the line see Shawn Steward's answer.