Search a string from text file & Return the Line Number using VBA - excel

I have one text file that contains around 100K lines. Now I would like to search a string from the text file. If that string is present then I want to get the line number at which it's present. At the end I need all the occurrence of that string with line numbers from the text file.
* Ordinary Method Tried *
We can read the whole text file line by line. Keep a counter variable that increases after every read. If I found my string then I will return the Counter Variable. The limitation of this method is, I have to traverse through all the 100K lines one by one to search the string. This will decrease the performance.
* Quick Method (HELP REQUIRED)*
Is there any way that will directly take me to the line where my searchstring is present and if found I can return the line number where it's present.
* Example *
Consider below data is present in text file. (say only 5 lines are present)
Now I would like to search a string say "Pune". Now after search, it should return me Line number where string "pune" is present. Here in this case it's present in line 2. I should get "2" as an output. I would like to search all the occurrence of "pune" with their line numbers

I used a spin off of Me How's code example to go through a list of ~10,000 files searching for a string. Plus, since my html files have the potential to contain the string on several lines, and I wanted a staggered output, I changed it up a bit and added the cell insertion piece. I'm just learning, but this did exactly what I needed and I hope it can help others.
Public Sub ReadTxtFile()
Dim start As Date
start = Now
Dim oFSO As Object
Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim oFS As Object
Dim filePath As String
Dim a, b, c, d, e As Integer
a = 2
b = 2
c = 3
d = 2
e = 1
Dim arr() As String
Do While Cells(d, e) <> vbNullString
filePath = Cells(d, e)
ReDim arr(5000) As String
Dim i As Long
i = 0
If oFSO.FileExists(filePath) Then
On Error GoTo Err
Set oFS = oFSO.OpenTextFile(filePath)
Do While Not oFS.AtEndOfStream
arr(i) = oFS.ReadLine
i = i + 1
Loop
oFS.Close
Else
MsgBox "The file path is invalid.", vbCritical, vbNullString
Exit Sub
End If
For i = LBound(arr) To UBound(arr)
If InStr(1, arr(i), "Clipboard", vbTextCompare) Then
Debug.Print i + 1, arr(i)
Cells(a + 1, b - 1).Select
Selection.Insert Shift:=xlDown
Cells(a, b).Value = i + 1
Cells(a, c).Value = arr(i)
a = a + 1
d = d + 1
End If
Next
a = a + 1
d = d + 1
Loop
Debug.Print DateDiff("s", start, Now)
Exit Sub
Err:
MsgBox "Error while reading the file.", vbCritical, vbNullString
oFS.Close
Exit Sub
End Sub

the following fragment could be repalaced like:
Dim arr() As String
Dim i As Long
i = 0
If oFSO.FileExists(filePath) Then
On Error GoTo Err
Set oFS = oFSO.OpenTextFile(filePath)
Do While Not oFS.AtEndOfStream
ReDim Preserve arr(0 To i)
arr(i) = oFS.ReadLine 'to save line's content to array
'If Len(oFSfile.ReadLine) = 0 Then Exit Do 'to get number of lines only
i = i + 1
Loop
oFS.Close
Else
MsgBox "The file path is invalid.", vbCritical, vbNullString
Exit Sub
End If

Here's another method that should work fairly quickly. It uses the shell to execute the FINDSTR command. If you find the cmd box flickers, do an internet search for how to disable it. There are two options provided: one will return the line number followed by a colon and the text of the line containing the keyword. The other will just return the line number.
Not sure what you want to do with the results, so I just have them in a message box.
Option Explicit
'Set reference to Windows Script Host Object Model
Sub FindStrings()
Const FindStr As String = "Pune"
Const FN As String = "C:\users\ron\desktop\LineNumTest.txt"
Dim WSH As WshShell
Dim StdOut As Object
Dim S As String
Set WSH = New WshShell
Set StdOut = WSH.Exec("cmd /c findstr /N " & FindStr & Space(1) & FN).StdOut
Do Until StdOut.AtEndOfStream
S = S & vbCrLf & StdOut.ReadLine
'If you want ONLY the line number, then
'S = S & vbCrLf & Split(StdOut.ReadLine, ":")(0)
Loop
S = Mid(S, 2)
MsgBox (S)
End Sub

Related

VBA script to run batch file from excel list, read result file, parse result file and write result to primary excel file

So, before I place my code, I'll explain what I am trying to do, because I can't test the script myself due to what it is supposed to do, effecting what it must do. I know this is a bit odd, but bear with me please.
Once every two weeks or so, we currently run batch files to update a specific tool on all the WS's in our organization.
Yes, we do have tool propagation software, but as this specific tool is extremely important, we don't trust it's distribution to any automated method which have proven in most cases to fail without us being able to understand the reason.
So, I wrote a few simple command batch files which run the installation command, and write the output to a text file which we then manually go through to find which ws's it was installed on, and which it wasn't.
The ws's on which it was not installed are the ws's we know we know due to the failure, that we have additional issues with and we then put all our effort into finding and fixing those issues.
As you can imagine, it's a time consuming effort, and I have decided I want to automate as much as possible of the manual check, in order to know quickly which ws's failed, and the fail code.
I start out with a list of ws names in excel.
For example,
K190ASSn1.domainname
m930eastgate.domainname
n190alka.domainname
n190amsv.domainname
n190amzi.domainname
N190ARME.domainname
N190AVMA.domainname
N190AVNT.domainname
n190chockstest.domainname
N190DLCR.domainname
N190DNBS.domainname
N190edsh.domainname
n190ehma2.domainname
N190EISH.domainname
I wrote my script to do the following:
Read all the ws names from column A into an array.
Loop through the array, and use the Shell function to call an external cmd file which then runs, and writes the result of the run into a TXT file located in a directory on the D drive called "Minstall".
I then read the names of all the files created in that directory into a new array.
I sort both arrays from A to Z (using a script I found online) to get everything in the same order for the next stage.
I then loop through the file names in the 2nd array, and read each file into a text field which I then parse to find the result of the script run.
That result is then written into a third array in the same logical position of the file name I read.
Finally, I re-write the file names back to the worksheet, overwriting what was there, and in the adjacent column, I write the run result from the relevant cell position in the third array.
I will then end up with a file that contains all the data in one visible point (I hope).
At a later stage, I will add a script that will email the relevant team with a list of the ws's they need to deal with (Those with any run result different from zero), and what they need to do. But that's not for the here and now.
Since if I run the code and it works (I hope) it would perform the update, and I do not yet want to do that, what I am really looking for, is additional eyes to go over my code, to see if what I wrote for each action as defined above is correct and will work, and if there is a way to perhaps write what I did, better.
In general, I went over each stage and everything "looks" good.
Anyone willing to assist here ?
Added by request of #CDP1802:
Examples of the two different results that can be found in the text files. One contains a result of zero, meaning that the script worked. The other contains a code of 1603, which is a generic "there's a problem captain but I don't know what it is" response from M$ msiexec.
The spaces between the lines of the text are what appear in the actual text file.
Example 1 (0 response)
PsExec v2.33 - Execute processes remotely
Copyright (C) 2001-2021 Mark Russinovich
Sysinternals - www.sysinternals.com
C:\Windows\system32>msiexec /i "\\server\Minstall\Installation.msi" /qn ACCEPTEULA=YES REPORTSERVER=server.domainname USESSL=TRUE REPORTSERVERPORT=443 IGNORESSLERRORS=TRUE InstallCertificates=yes /l*v C:\Windows\TEMP\install_log4258289.txt
Connecting to K190LPRTLV4.iaadom...
Starting PSEXESVC service on K190LPRTLV4.iaadom...
Copying authentication key to K190LPRTLV4.iaadom...
Connecting with PsExec service on K190LPRTLV4.iaadom...
Copying d:\Install425.bat to K190LPRTLV4.iaadom...
Starting d:\Install425.bat on K190LPRTLV4.iaadom...
Install425.bat exited on K190LPRTLV4.iaadom with error code 0.
Example 2 (1603 response)
PsExec v2.33 - Execute processes remotely
Copyright (C) 2001-2021 Mark Russinovich
Sysinternals - www.sysinternals.com
C:\Windows\system32>msiexec /i "\\server\Minstall\Installation.msi" /qn ACCEPTEULA=YES REPORTSERVER=server.domainname USESSL=TRUE REPORTSERVERPORT=443 IGNORESSLERRORS=TRUE InstallCertificates=yes /l*v C:\Windows\TEMP\install_log4258289.txt
Connecting to K190LPRTLV3.iaadom...
Starting PSEXESVC service on K190LPRTLV3.iaadom...
Copying authentication key to K190LPRTLV3.iaadom...
Connecting with PsExec service on K190LPRTLV3.iaadom...
Copying d:\Install425.bat to K190LPRTLV3.iaadom...
Starting d:\Install425.bat on K190LPRTLV3.iaadom...
Install425.bat exited on K190LPRTLV3.iaadom with error code 1603.
The updated code is as follows:
Option Explicit
Sub Check_Files()
Const Col_Names = "A"
Const Col_Result = "B"
Const Row_Text = 4 'first line of text and result
Dim wb As Workbook
Dim wsMain As Worksheet
Dim WSNames() As String 'Will hold all the ws names as an array read from column A
Dim WSResult() 'Will hold result for specific ws
Dim DirectoryListArray() As string
ReDim DirectoryListArray(3000) 'Set the directory listing array size to 3000 as a max count
Dim NumberArray() As Long
Dim lastrow As Long, FileCount As Long, NumberCount As Long, r As Long, i As Long, j As Long
Dim awsname as string, strDir As string, strPath As string
Dim item as variant
Dim ReadFile As String, text As String, textline As String, RetCode As Integer
Set wb = ActiveWorkbook
With wb
Set wsMain = .Sheets("Main")
End With
'Copy ws names into array for speed
With wsMain
lastrow = .Cells(.Rows.Count, Col_Names).End(xlUp).Row
If lastrow < Row_Text Then
MsgBox "No ws names found in column " & Col_Names, vbCritical
Exit Sub
End If
WSNames = .Cells(1, Col_Names).Resize(lastrow).Value2
ReDim WSResult(1 To lastrow)
End With
'Write how many names were read into array
Cells(1,3) = "Number of names read into array is " & lastrow
'loop through all ws names and run the batch file for each one
For r = Row_Text To UBound(WSNames)
awsname = WSNames(r, 1) 'Read in next ws name from array
Runcmd(awsname)
Next r
'Write how many batch files were run into worksheet
Cells(2,3) = "Number of batch files run is " & r
'count how many text files have been created
strDir = "D:\Minstall"
strPath = strDir & "\*.txt"
'Loop through all the files in the directory by using Dir$ function
MyFile = Dir$(strPath)
Do While MyFile <> ""
DirectoryListArray(FileCount) = MyFile
MyFile = Dir$
FileCount = FileCount + 1
Loop
'Reset the size of the array without losing its values by using Redim Preserve
Redim Preserve DirectoryListArray(FileCount - 1)
'Write how many text files were found
Cells(3,3) = "Number of txt files found is " & FileCount
''Debug.Print writes the results to the Immediate window (press Ctrl + G to view it)
'For FileCount = 0 To UBound(DirectoryListArray)
'Debug.Print DirectoryListArray(FileCount)
'Next FileCount
'Sort the arrays so that we have the same order in both arrays
'Since both arrays should in effect have the same amount of elements
'sorting names array from A to Z
For i = LBound(WSNames) To UBound(WSNames)
For j = i + 1 To UBound(WSNames)
If UCase(WSNames(i,1)) > UCase(WSNames(j,1)) Then
Temp = WSNames(j,1)
WSNames(j,1) = WSNames(i,1)
WSNames(i,1) = Temp
End If
Next j
Next i
'sorting file array from A to Z
For i = LBound(DirectoryListArray) To UBound(DirectoryListArray)
For j = i + 1 To UBound(DirectoryListArray)
If UCase(DirectoryListArray(i,1)) > UCase(DirectoryListArray(j,1)) Then
Temp = DirectoryListArray(j,1)
DirectoryListArray(j,1) = DirectoryListArray(i,1)
DirectoryListArray(i,1) = Temp
End If
Next j
Next i
NumberCount = 0
'Loop through files in directory based on what's in array
For i = LBound(DirectoryListArray) To UBound(DirectoryListArray)
ReadFile = "D:\Minstall" & "\" & DirectoryListArray(NumberCount)
ReadFile = Application.GetOpenFilename()
Open myFile For Input As #1
Do Until EOF(1)
Line Input #1, textline
text = text & textline
Loop
Close #1
RetCode = InStr(text, "with error code ")
NumFound = Mid(text, posLat + 16, 1)
If NumFound > 0 Then
NumFound = Mid(text, posLat + 16, 4)
'Write the number found into the number array
NumberArray(NumberCount) = NumFound
NumberCount = NumberCount + 1
Else
'Write the number found into the number array
NumberArray(NumberCount) = NumFound
NumberCount = NumberCount + 1
End If
Next i
'Write the ws name into the worksheet and write the number found to the cell to the right of the ws name in the worksheet
For i = LBound(WSNames) To UBound(WSNames)
Cells(j, Col_Names) = WSNames(i,1)
Cells(j, Col_Result) = NumberCount(i,1)
j = j + 1
Next i
End Sub
Sub Runcmd(awsname)
Dim PathToBatch as string
'Set the path and batch file with the ws name as a parameter for the batch to run
PathToBatch = "D:\min425.cmd" & " " & awsname
Call Shell(PathToBatch, vbNormalFocus)
End Sub
The main changes are using a FileSystemObject to read the text files, a Regular Expression to extract the error code, and a WScript.Shell object to run the batch file so macro waits for the script to complete. I have commented out the RunCmd line and replaced it with a RunTest that creates a text file so you can test it.
Option Explicit
Sub Check_Files()
Const DIR_OUT = "D:\Minstall"
Const COL_NAMES = "A"
Const COL_RESULTS = "B"
Const COL_TS = "C" ' timestamp
Const COL_ERR = "D" ' Shell errors
Const ROW_START = 4 'first line of text and result
Dim wb As Workbook, ws As Worksheet
Dim rng As Range, arNames, awsname As String
Dim result As String, txtfile As String
Dim i As Long, LastRow As Long, n As Long, r As Long, colour As Long
Dim t0 As Single: t0 = Timer
Set wb = ThisWorkbook
Set ws = wb.Sheets("Main")
With ws
' read names into array
LastRow = .Cells(.Rows.Count, COL_NAMES).End(xlUp).Row
n = LastRow - ROW_START + 1
If n < 1 Then
MsgBox "No records found on " & ws.Name, vbCritical
Exit Sub
Else
Set rng = .Cells(ROW_START, COL_NAMES).Resize(n)
arNames = rng.Value2
'Write how many names were read into array
.Cells(1, 3) = "Number of names read into array is " & n
End If
' clear results
With rng.Offset(, 1).Resize(, 3)
.Clear
.Interior.Pattern = xlNone
End With
' run commands with WsSCript
Dim WShell As Object
Set WShell = CreateObject("WScript.Shell")
For i = 1 To UBound(arNames)
awsname = arNames(i, 1)
r = ROW_START + i - 1
' RUN COMMANDS
.Cells(r, COL_ERR) = RunTest(awsname, DIR_OUT)
'.Cells(r, COL_ERR) = RunCmd(WShell, awsname, DIR_OUT)
.Cells(r, COL_TS) = Format(Now, "yyyy-mm-dd HH:MM:SS") ' timestamp
Next
Set WShell = Nothing
'Write how many batch files were run into worksheet
.Cells(2, 3) = "Number of batch files run is " & UBound(arNames)
' read text files with FSO, parse with regex
Dim FSO As Object, ts As Object, regex As Object, txt As String
Set FSO = CreateObject("Scripting.FileSystemObject")
Set regex = CreateObject("VBScript.RegExp")
With regex
.Global = False
.MultiLine = True
.IgnoreCase = True
.Pattern = "with error code (\d+)"
End With
n = 0
' process text file
For i = 1 To UBound(arNames)
r = ROW_START + i - 1
awsname = arNames(i, 1)
txtfile = DIR_OUT & awsname & ".txt"
result = ""
' does file exist for this machine
If FSO.fileExists(txtfile) Then
' read file
n = n + 1
Set ts = FSO.openTextfile(txtfile)
txt = ts.readall
ts.Close
' extract error number from text
If regex.test(txt) Then
result = regex.Execute(txt)(0).submatches(0)
End If
' error codes
If result = "0" Then
colour = RGB(0, 255, 0) ' green
Else
colour = RGB(255, 255, 0) ' yellow
End If
Else
result = "No Text File"
colour = RGB(255, 0, 0) ' red
End If
' result
With .Cells(r, COL_RESULTS)
.Value2 = result
.Interior.Color = colour
End With
Next
.Cells(3, 3) = "Number of txt files found is " & n
.Columns.AutoFit
End With
MsgBox "Text files found for " & n, vbInformation, Format(Timer - t0, "0.0 secs")
End Sub
Function RunTest(awsname As String, folder As String) As String
Dim FSO, ts, errno: Set FSO = CreateObject("Scripting.FileSystemObject")
If Rnd() < 0.3 Then errno = 0 Else errno = Int(10000 * Rnd())
Set ts = FSO.createTextFile(folder & awsname & ".txt")
ts.write "This is with error code " & errno & "." & vbCrLf & vbCrLf
ts.Close
RunTest = "Test"
End Function
Function RunCmd(WShell, awsname As String, folder As String) As String
MsgBox "RunCmd DISABLED", vbCritical: End
'Const SCRIPT = "D:\min425.cmd"
'Dim cmd: cmd = SCRIPT & " " & awsname
'RunCmd = WShell.Run(cmd, vbNormal, True) ' waittocomplete
End Function

VBA Object with variable or block variable not set error even when is already set

I am currently trying to set conditions such that when a CSV file is not found in the folder, it will continue to find other CSV files. However I'm facing the "object with variable or block variable not set" error at the 2nd private sub readdatavcap2 even when I've already set Set o_file = fs2.OpenTextFile for both 1st and 2nd sub. I'm confused because for the 1st sub, the error does not occurs at o_file.Close after the else statement while for 2nd sub it occurs. Does anybody knows why?
Private Sub readdatavcap1(filename As String, i As Integer)
Application.ScreenUpdating = False
Dim sl As String
Dim first As Integer
Dim second As Integer
Dim j As Long
Dim fs2 As New Scripting.FileSystemObject
Dim o_file As Scripting.TextStream
j = 2 'variable not defined at fs2
If Dir(filename) <> "" Then
Set fs2 = CreateObject("Scripting.FileSystemObject") 'FileSystemObject also called as FSO, provides an easy object based model to access computer's file system.
'o_file contains filename(csv file link)
Set o_file = fs2.OpenTextFile(filename, 1, TristateFalse) '1=Open a file for reading only. You can't write to this file. TristateFalse means u get ascii file by default
'2=ForWriting, 8= Forappending
'o_file contains filename(text file data)
sl = o_file.readline 'Reads an entire line (up to, but not including, the newline character) from a TextStream file and returns the resulting string.
Do While Left(sl, 1) = "#" 'Left Function is used to extract N number of characters from a string from the left side.
sl = o_file.readline
Loop
Do While o_file.atendofstream <> True 'atendofstream = Read-only property that returns True if the file pointer is at the end of a TextStream file; False if it is not.
sl = o_file.readline
first = InStr(32, sl, ",", 1) - 15 'INSTR function returns the position of the first occurrence of a substring in a string.
second = InStr(first + 2, sl, ",", 1) 'syntax of InStr( [start], string, substring, [compare] )
'start sets string position for each search, string = string being search, substring= string expression searched ,
'eg:InStr(1, "Tech on the Net", "t") Result: 9 'Shows that search is case-sensitive
'compare= optional 1= textcompare
'searching for commas in the file in this case
If second = 0 Then
second = Len(sl) + 1 'len=length of file string
End If
If tddb_vramp = True Then
' write the Voltage Ramp to stress part
If i = 2 Then
ActiveWorkbook.Sheets("Ramp_current").Cells(j, 1) = Mid(sl, 2, first - 2)
End If
ActiveWorkbook.Sheets("Ramp_current").Cells(j, i) = Abs(Mid(sl, first + 2, second - 2 - first)) + 0.000000000000001
Else
'Write the normal current trace
ActiveWorkbook.Sheets("currentPTN_STI").Cells(j, 2 * i - 3) = Mid(sl, 15, first - 14)
' The MID function returns the specified number of characters in a text string, starting from a specified position (
'ie. starting from a specified character number).
'Use this function to extract a sub-string from any part of a text string. Syntax: MID(text_string, start_number, char_numbers).
ActiveWorkbook.Sheets("currentPTN_STI").Cells(j, 2 * i - 2) = Abs(Mid(sl, first + 2, second - 2 - first)) + 0.000000000000001
End If
j = j + 1
Loop
If tddb_vramp = False Then
myarray(i) = j - 1
End If
o_file.Close
Else
o_file.Close
End If
End Sub
Private Sub readdatavcap2(filename As String, i As Integer)
(rest of the code same as readdatavcap1)
.
.
.
o_file.Close
Else
o_file.Close <---error occurs here
End If
End Sub
I worked my way through your code but can't do more than confirm what GSerg already said in his first comment, i.e. you can't close a file that isn't open.
Option Explicit
Sub Main()
Dim SourceFolder As String
Dim Fn As String ' Filoe name
Dim i As Integer
With Application.FileDialog(msoFileDialogFolderPicker)
If .Show = -1 Then ' if OK is pressed
SourceFolder = .SelectedItems(1)
End If
End With
If SourceFolder <> "" Then ' a folder was chosen
i = 2
Fn = Dir(SourceFolder & "\*.csv")
Do While Len(Fn) > 0
readdatavcap1 Fn, i
Fn = Dir
Loop
End If
End Sub
Private Sub readdatavcap1(filename As String, i As Integer)
' "filename" is a variable used by VBA
' your use of it may cause unexpected problems.
' to check, select the name and press F1.
Dim sl As String
Dim first As Integer
Dim second As Integer
Dim j As Long
Dim fs2 As New Scripting.FileSystemObject
Dim o_file As Scripting.TextStream
Dim tddb_vramp As Boolean
If Dir(filename) <> "" Then
Application.ScreenUpdating = False
j = 2 'variable not defined at fs2
' FileSystemObject also called as FSO, provides an easy object based model
' to access computer's file system.
Set fs2 = CreateObject("Scripting.FileSystemObject")
' o_file contains filename (csv file link)
' 1=Open a file for reading only. You can't write to this file.
' 2=ForWriting, 8= For appending
' TristateFalse means u get ascii file by default.
Set o_file = fs2.OpenTextFile(filename, 1, TristateFalse)
' o_file contains filename(text file data)
' Reads an entire line (up to, but not including, the newline character)
' from a TextStream file and returns the resulting string.
sl = o_file.readline
Do While Left(sl, 1) = "#"
' Left Function is used to extract N number of characters from a string from the left side.
sl = o_file.readline
Loop
' atendofstream = Read-only property that returns True if the file pointer
' is at the end of a TextStream file; False if it is not.
Do While o_file.atendofstream <> True
sl = o_file.readline
' INSTR function returns the position of the first occurrence of a substring in a string.
' syntax of InStr( [start], string, substring, [compare] )
' start sets string position for each search, string = string being search,
' substring= string expression searched ,
' eg:InStr(1, "Tech on the Net", "t") Result: 9
' Shows that search is case-sensitive
' compare= optional 1= textcompare
' searching for commas in the file in this case
first = InStr(32, sl, ",", 1) - 15 ' what if first is negative?
second = InStr(first + 2, sl, ",", 1)
If second = 0 Then
second = Len(sl) + 1 'len=length of file string
End If
' "ActiveWorkbook" seems not necessary unless you intend to have
' several workbooks, all having a sheet "Ramp_current" open at the
' same time, and none of them being ThisWorkbook.
' But if that's your intention "ActiveWorkbook" will lead to
' disaster sooner rather than later.
If tddb_vramp = True Then
' write the Voltage Ramp to stress part
If i = 2 Then
ActiveWorkbook.Sheets("Ramp_current").Cells(j, 1) = Mid(sl, 2, first - 2)
End If
ActiveWorkbook.Sheets("Ramp_current").Cells(j, i) = Abs(Mid(sl, first + 2, second - 2 - first)) + 0.000000000000001
Else
'Write the normal current trace
ActiveWorkbook.Sheets("currentPTN_STI").Cells(j, 2 * i - 3) = Mid(sl, 15, first - 14)
' The MID function returns the specified number of characters in a text string,
' starting from a specified position (ie. starting from a specified character number).
' Use this function to extract a sub-string from any part of a text string.
' Syntax: MID(text_string, start_number, char_numbers).
ActiveWorkbook.Sheets("currentPTN_STI").Cells(j, 2 * i - 2) = Abs(Mid(sl, first + 2, second - 2 - first)) + 0.000000000000001
End If
j = j + 1
Loop
If tddb_vramp = False Then
myarray(i) = j - 1
End If
o_file.Close
Application.ScreenUpdating = True
Else
' if Dir(filename) = "" The o_file doesn't exist
MsgBox filename & " wasn't found.", _
vbInformation, "Reading failure"
End If
End Sub
You should remove the Else condition from the above code. If you do that the code will do exactly nothing if the file isn't found. This fact would probably induce me to convert this procedure into a function that returns True if the file was found and False if it isn't. Perhaps that's helpful.
The point is that this procedure must be called by a Main proc which loops through all the files in a folder (for example) calling your proc with different file names. So, if your proc returns False the Main might issue a message saying that a file wasn't found. But even if you don't care for that, it's the Main that would select the next file after one has either been found and evaluated or not.

Excel - Generating output Word file from Word template documents

I have a simple excel VBA routine to use template text files and replace key tags in them with values from an Excel array, with variable rows/columns. It works great, and has saved me tons of time for the last couple of years.
Now I need to do the same thing, but read/export a word document.
It's KILLING me. I've tried to follow numerous examples, but all I get is an output file that's the un-modified template pages that I'm using; all the original keywords that I'm searching for, but none of the replacements, even when my debug feed is showing positive hits for all keys.
Public Sub LogicGen(ActiveSheet As String)
On Error Resume Next
DebugMode = True 'Prints some extra data to the debugger window
'Variables
Dim Filename As String
Dim WorkbookPath As String
Dim KeyInput As Variant
Dim i As Integer
Dim END_OF_STORY
Dim MOVE_SELECTION
END_OF_STORY = 6
MOVE_SELECTION = 0
'Activate a worksheet
Worksheets(ActiveSheet).Activate
'Figure out how many keys were entered
i = 2
KeyInput = Cells(6, i)
Do Until IsEmpty(KeyInput)
i = i + 1
KeyInput = Cells(6, i)
Loop
' Key count is the empty address minus 2
KeyCount = i - 2
' push those keys into an array
Dim KeyArray() As String
ReDim KeyArray(0 To KeyCount) As String
For i = LBound(KeyArray) To UBound(KeyArray)
KeyArray(i) = Cells(6, i + 2)
If DebugMode Then
'Debug.Print KeyArray(i)
End If
Next i
'KeyArray now has all of the key values, which will be reused for each of the tags
WorkbookPath = ActiveWorkbook.Path
'Determine how many rows are populated by counting the template cells
TemplateInput = Cells(7, 1)
RowCount = 0
Do Until IsEmpty(TemplateInput)
RowCount = RowCount + 1
TemplateInput = Cells(RowCount + 7, 2)
Loop
OutputFilePath = WorkbookPath & "\" & Cells(1, 2)
'Create an output file
On Error Resume Next
Set OutputApp = GetObject(, "Word.Application")
If Err.Number <> 0 Then
Set OutputApp = CreateObject("word.application")
End If
On Error GoTo 0
Set OutputDoc = OutputApp.Documents.Add
Set OutputSelection = OutputApp.Selection
'build a Build a 2D array for the tag values, with the associated
'tag values.
Dim TagArray() As String
ReDim TagArray(0 To RowCount, 0 To KeyCount)
' Step down through all of the rows that have been entered
For i = 0 To RowCount - 1
'Build an array of all of the tags
For KeyIndex = 0 To KeyCount
TagArray(i, KeyIndex) = Cells(i + 7, KeyIndex + 2).Text
If DebugMode Then
'Debug.Print TagArray(i, KeyIndex)
End If
Next KeyIndex
'Ensure template file exists, once per row
Filename = WorkbookPath & "\" & Cells(i + 7, 1).Text
' Check for existance of template file, and open if it exists
If Not FileFolderExists(Filename) Then
MsgBox (Filename & " does not exist")
GoTo EarlyExit
Else
'Grab the template file and push it to the output
Set TemplateApp = CreateObject("word.application")
Set TemplateDoc = TemplateApp.Documents.Open(Filename)
Set TemplateSel = TemplateApp.Selection
TemplateDoc.Range.Select
TemplateDoc.Range.Copy
OutputSelection.endkey END_OF_STORY, MOVE_SELECTION
OutputSelection.TypeParagraph
OutputSelection.Paste
'Clear the template file, since we don't know if it will be the same next time
TemplateDoc.Close
TemplateApp.Quit
Set TemplateApp = Nothing
End If
'Iterate through all of the keys to be replaced
For j = 0 To KeyCount - 1
For Each storyrange In OutputDoc.StoryRanges
Do
With storyrange.Find
.Text = KeyArray(j)
.Replacement.Text = TagArray(i, j)
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
If .Execute(Replace:=wdReplaceAll) Then
Debug.Print "Replacing: " & KeyArray(j) & " With: " & TagArray(i, j)
End If
End With
Set storyrange = storyrange.nextstoryrange
Loop While Not storyrange Is Nothing
Next
Next j
Next i
OutputDoc.SaveAs (OutputFilePath)
EarlyExit:
' Close the files that were opened
OutputDoc.Close
OutputApp.Quit
Set OutputDoc = Nothing
Even though my debug monitor is full of stuff like:
Replacing: %EULow% With: 0
Replacing: %EUHigh% With: 100
Replacing: %AlarmHH% With: No HH
Replacing: %AlarmH% With: No H
Replacing: %AlarmL% With: No L
My output document is still numerous pages of Word tables with the %something% tags not replaced. I'm going mad - I've been working on this all day.
This is where it's breaking down:
For Each storyrange In OutputDoc.StoryRanges
Do
With storyrange.Find
.Text = KeyArray(j)
.Replacement.Text = TagArray(i, j)
.Wrap = wdFindContinue
.Execute Replace:=wdReplaceAll
If .Execute(Replace:=wdReplaceAll) Then
Debug.Print "Replacing: " & KeyArray(j) & " With: " & TagArray(i, j)
End If
End With
Set storyrange = storyrange.nextstoryrange
Loop While Not storyrange Is Nothing
Next
I've tried to do this search and replace probably 7 different ways from different examples, nothing actually replaces the text.
The problem is almost certainly that you are using "late binding" (which is fine), and are not referencing the Word object model, which means that constants defined by the Word Object model such as "wdFindContinue" and "wdReplaceAll" will be "empty". The values in the Word Object model are 1 and 2, respectively.
You can either reference the Word object model (there are advantages and disadvantages of doing so) via VB Editor's Tools->References menu, and reference the constants in it, or define your own constants with the same name and the correct values, or just use the correct constant values.
If you choose to reference the Word Object model, VBA should pick up the Word constant values with no additional qualification, i.e.
debug.print wdReplaceAll
should now display "2" in the Immediate window>
However, some people prefer to spell out the origin of these constants, e.g. via
Word.wdReplaceAll
or to be even more specific
Word.wdReplace.wdReplaceAll
If you want to see the Debug.Print output, you should also delete the first .Execute Replace:=ReplaceAll in your code (because it will then work properly, so the search string will not be found when the second .Execute method is called).

Read from all text files in folder, check for matches and insert text file value into Excel sheet

I am attempting to work on a code which will allow me to check two lines of all my text files in a folder.
Each text file will be structured like so:
NS1234 <--- A random reference number on the first line
Approve < Reject or Approve on the second line
At the moment the code only reads one text file which I specify the name of, however I want it to scan all .txt files.
Next, When I open my spread sheet I will have the following set-up:
Column A Column
NS1234
I want my code to scan all text files to check for any matching reference number from column A against all the text files.
And then where a match is found insert either 'Approve' or 'Reject', where this is written on the second line of the text file, into the corresponding row in column s
Code:
Public Sub test()
Dim fn As Integer
fn = FreeFile
Open "Z:\NS\Approval\NS32D1QR.txt" For Input As fn
Dim wholeFile As String
wholeFile = Input(LOF(fn), #fn)
Close #fn
Dim splitArray
splitArray = Split(wholeFile, vbCrLf)
Dim lineNum As Integer
lineNum = 2
Dim i As Integer, intValueToFind As Integer
intValueToFind = NS32D1QR
For i = 1 To 500 ' Revise the 500 to include all of your values
If Cells(i, 1).Value = intValueToFind And splitArray(lineNum - 1) = "Approve" Then
Range("S" & ActiveCell.Row).Value = "Approve"
End If
Next i
End Sub
i 'm not sure about the test that you made in your loop but it seems to me that the info where on the 2 first lines so no use to loop or to use special variables there. Let me know if this work properly or not! ;)
Here is a sub to test, as it is a function you can either loop on it or use it in Excel workbook.
Sub test()
With Sheets("Sheet1")
For i = 2 To .Rows(.Rows.Count).End(xlUp).Row
.Cells(i, "S") = Get_AorP(.Cells(i, "A"))
Next i
End With
End Sub
Here is what you wanted to do, converted to a function :
Public Function Get_AorP(ByVal Value_to_Find As String) As String
Dim fn As Integer, _
Txts_Folder_Path As String, _
File_Name As String, _
wholeFile As String, _
splitArray() As String, _
i As Integer
On Error GoTo ErrHandler
Txts_Folder_Path = "Z:\NS\Approval\"
File_Name = Dir(Txts_Folder_Path & "*.txt")
While File_Name <> vbNullString
fn = FreeFile
Open Txts_Folder_Path & File_Name For Input As fn
wholeFile = Input(LOF(fn), #fn)
Close #fn
MsgBox File_Name
splitArray = Split(wholeFile, vbCrLf)
If UBound(splitArray) < 2 Or LBound(splitArray) > 1 Then
'avoid empty text files
Else
If InStr(1, splitArray(0), Value_to_Find) <> 0 Then
If InStr(1, splitArray(1), "Approve") Then
Get_AorP = "Approve"
Exit Function
Else
If InStr(1, splitArray(1), "Reject") Then
Get_AorP = "Reject"
Exit Function
Else
'Nothing to do
End If
End If
Else
'not the good value
End If
End If
File_Name = Dir()
Wend
Get_AorP = "No matches found"
Exit Function
ErrHandler:
Get_AorP = "Error during the import." & vbCrLf & Err.Number & " : " & Err.Description
End Function

How to search for more than one string in VB6?

I want to search for more than one string in a file with vb6
using instr we can do it for single string but I don't know how to use instr for more than one string now how can I search for more than one and if find one of them we receive a message?
Open file For Binary As #1
strData = Space$(FileLen(file))
Get #1, , strData
Close #1
lngFind = InStr(1, strData, string)
That's simply a case of introducing multiple tests for multiple strings...
Dim strArray(10) As String
DIm cntArray(10) As Integer
Dim strData As String
Dim c As Integer
'Set-up your search strings...
...
Open file For Binary As #1
Get #1, , strData
Close #1
For c = 1 to 10
cntArray(c) = Instr(strData, strArray(c))
Next c
If all you want to do is show a true or false message box then we don't need to assign the value to the second array. The For loop could be replaced with...
For c = 1 to 10
If Instr(strData, strArray(c)) > 0 Then
MsgBox "'" & strArray(c) & "' found in file."
'Remove the following line if you want everything to be searched for,
'but leave it in if you only want the first string found...
Exit For
End If
Next c
Really this is a very basic piece of code. If you're looking to write code as anything but a novice then you need to research the commands, functions and structures included in this post. A good place to start, for a complete novice, would be somewhere like http://www.thevbprogrammer.com/classic_vbtutorials.asp or http://www.vb6.us/.
'-----------------------------------------------------------
'perform multiple instr on a string. returns true if all instr pass
'-----------------------------------------------------------
Function bMultiInstr(sToInspect As String, ParamArray sArrConditions()) As Boolean
On Error GoTo err:
Dim i As Integer, iUpp As Integer
iUpp = UBound(sArrConditions) 'instr conditions
For i = 0 To iUpp ' loop them
If InStr(1, sToInspect, sArrConditions(i)) <= 0 Then Exit Function ' if instr returns 0 then exit - [bPasses] will be left false
Next i
bPasses = True
Exit Function
err:
With err
If .Number <> 0 Then
'create .bas named [ErrHandler] see http://vb6.info/h764u
ErrHandler.ReportError Date & ": Strings.bMultiInstr." & err.Number & "." & err.Description
Resume Next
End If
End With
End Function
That is from http://vb6.info/string/instr-multi-perform-instr-checks-multiple-inst-conditions-function/

Resources