File System Object VB - vba

Sub Command3_Click()
Dim fs As FileSystemObject
Dim f As TextStream
Dim a As Variant
Dim i As Long
Set fs = CreateObject("Scripting.FileSystemObject")
' Read file into array
If fs.FileExists("C:\rbc.csv") Then
Set f = fs.OpenTextFile("C:\rbc.csv", ForReading, False)
a = Split(f.ReadAll, vbNewLine, -1, vbTextCompare)
f.Close
Else
MsgBox "The file path is invalid.", vbCritical, vbNullString
Exit Sub
End If
' Write line > 1 to file
Set f = fs.OpenTextFile("C:\rbc.csv", ForWriting, True)
For i = 1 To UBound(a)
f.WriteLine a(i)
Next
f.Close
End Sub
It worked fine when I tried it on csv files , but then I had a problem . One of the files has the first line as " A,B,C,D, " (NO Quotes ) and second file has first line as
" 01-JUL-2014,RBC_BASELII_07012014,,,,,,,,,,,,,,,,,,, " .
Now when I try to delete the first line of the second file , the entire file text gets deleted , while it deletes only one line at a time in the first file. Please help me out.

Hopefully the comments explain what's going on.
Sub Command3_Click()
Dim fs As FileSystemObject
Dim f As TextStream
Dim a As Variant
Dim i As Long
Dim sLineBreak As String
Dim sAll As String
Const sFILE As String = "K:\rbc.csv"
Set fs = CreateObject("Scripting.FileSystemObject")
If fs.FileExists(sFILE) Then
Set f = fs.OpenTextFile(sFILE, ForReading, False)
sAll = f.ReadAll
'if there are cr's, then it's either only cr's or it's both
If InStr(1, sAll, vbCr) > 0 Then
a = Split(sAll, vbCr, -1, vbTextCompare)
Else 'no cr's means it's only line feeds
a = Split(sAll, vbLf, -1, vbTextCompare)
End If
f.Close
Else
MsgBox "The file path is invalid.", vbCritical, vbNullString
Exit Sub
End If
' Write line > 1 to file
Set f = fs.OpenTextFile(sFILE, ForWriting, True)
For i = 1 To UBound(a)
'if there are cr's, then we split on them, so they wouldn't
'be there anymore. But if it was both, there would be
'vblf's left over and we want to get rid of those
f.WriteLine Replace(a(i), vbLf, vbNullString)
Next
f.Close
End Sub

Related

How to get list of all subfolders in one folder and write it to txt file using vb

I want to know, how it possible to get list of all subfolders in "C/Windows" and write it to txt file. Here is my code:
Sub Check
MkDir "c:\New_Folder"
Dim iFileNo as Integer
Dim strFile As String
strFile = "c:\New_Folder\data.txt" 'the file you want to save to
intFile = FreeFile
Open strFile For Output As #intFile
Print #intFile,
Close #intFile
End Sub
Full Explanation: Write a program, like opening a folder on the D drive (the folder is your nickname). In this folder open the file data.txt, in which write down the names of all folders from the directory C: \ Windows. 2. Write a program that reads information from a file, which was opened with a first program and transfer through MsgBox skin another row to the file
Whenever a problem is defined as "get list of all subfolders" and "write to a text file", I know I likely need to implement a loop of some kind. As it turns out that is all that is missing from your code. The Dir command can help solve this problem:
Private Sub Check()
Dim intFile As Integer
Dim strFile As String
Dim FolderName As String
MkDir "c:\New_Folder"
strFile = "c:\New_Folder\data.txt"
intFile = FreeFile
Open strFile For Output As #intFile
FolderName = Dir("c:\windows\", vbDirectory)
Do While FolderName <> ""
If FolderName <> "." And FolderName <> ".." And (GetAttr("c:\windows\" & FolderName) And vbDirectory) = vbDirectory Then
Print #intFile, FolderName
End If
FolderName = Dir()
Loop
Close #intFile
End Sub
I would also encourage you to use proper formatting of your code, in this case indentation. It will make your life easier at some point!
A basic example with no error checking:
Sub Tester()
Dim f
For Each f In AllFolders("D:\Analysis")
Debug.Print f
Next f
End Sub
'return all folders which are subfolders of `startFolder`
Function AllFolders(startFolder As String)
Dim col As New Collection, colOut As New Collection, f, sf
col.Add startFolder
Do While col.Count > 0
f = col(1) & IIf(Right(f, 1) <> "\", "\", "")
col.Remove 1
sf = Dir(f, vbDirectory) 'fetch folders also
Do While Len(sf) > 0
If GetAttr(f & sf) = vbDirectory Then 'is this a folder ?
If sf <> "." And sf <> ".." Then 'ignore self or parent
col.Add f & sf & "\" 'add to list to check for subfolders
colOut.Add f & sf 'add to output
End If
End If
sf = Dir
Loop
Loop
Set AllFolders = colOut
End Function
Please, try the next code:
Sub testGetSubFolders()
Dim strFold As String, strFile As String, arrTxt
strFold = "C:\Windows"
If dir("c:\New_Folder", vbDirectory) = "" Then 'if the folder does not exist
MkDir "c:\New_Folder" 'it is created
End If
strFile = "c:\New_Folder\data.txt"
arrTxt = GetSubFolders(strFold) 'receive an array of subfolders
Open strFile For Output As #1
Print #1, Join(arrTxt, vbCrLf) 'join the array on end of line
Close #1
End Sub
Function GetSubFolders(strFold As String) As Variant 'it returns an array of subfolders path
Dim fso, fldr, subFldr, arr, i As Long
Set fso = CreateObject("Scripting.FileSystemObject")
Set fldr = fso.GetFolder(strFold)
ReDim arr(fldr.subFolders.count - 1) 'redim the array to keep the paths
For Each subFldr In fldr.subFolders
arr(i) = subFldr.Path: i = i + 1 'place the paths in the array and increment i
Next subFldr
GetSubFolders = arr
End Function

Rename text file based on first line of text

I have a list of text files (all different names.txt, but small files).
I want to rename them based on the first line of text in the file.
Some files have a few enters before the text. So the code returns with a (blank).txt.
Sub RenameTextFile()
Const SpecialCharacters As String = "\,/,:,*,?,<,>,|,""," ' Modify this as neccesary
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim fso, MyFile, FileName, TextLine
Dim char As Variant
Set fso = CreateObject("Scripting.FileSystemObject")
Set fol = fso.GetFolder("C:\Research syntheses - Meta analysis\Txt files ECS\out\")
For Each fil In fol.Files
FileName = fil
Set MyFile = fso.OpenTextFile(FileName, ForReading)
Do While MyFile.AtEndOfStream <> True
Do
Dim tmpLine As String
TextLine = MyFile.ReadLine
tmpLine = RemoveWhiteSpace(TextLine)
If Len(tmpLine) = 0 Then
TextLine = tmpLine
End If
Loop Until Len(TextLine) > 0
MyFile.Close
For Each char In Split(SpecialCharacters, ",")
TextLine = Replace(TextLine, char, "")
Next
fil.Name = TextLine & ".txt"
Exit Do
Loop
MyFile.Close
Next fil
End Sub
You could add another loop to your code like that
Sub RenameTextFile()
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim fso, MyFile, FileName, TextLine
Set fso = CreateObject("Scripting.FileSystemObject")
Set fol = fso.GetFolder("d:\tmp\")
For Each fil In fol.Files
FileName = fil
Set MyFile = fso.OpenTextFile(FileName, ForReading)
Do While MyFile.AtEndOfStream <> True
Do
Dim tmpLine As String
TextLine = MyFile.ReadLine
tmpLine = removeWhiteSpace(TextLine)
If Len(tmpLine) = 0 Then
TextLine = tmpLine
End If
Loop Until Len(TextLine) > 0
MyFile.Close
If isValidFilename(Trim(TextLine)) Then
On Error Resume Next
fil.Name = Trim(TextLine) & ".txt"
On Error Goto 0
Else
MsgBox "Renaming: " & fil.Name & " -to- " & Trim(TextLine) & " failed", vbCritical + vbOKOnly, "Invalid Filename"
End If
Exit Do
Loop
MyFile.Close
Next fil
End Sub
On the long run you need to think about kind of error handling as a file with the same name might already exist. And my extra loop will also fail in case one of the files only contains empty lines.
Update By the OP's comment I strongly guess that some of the files contain white spaces at the beginning and the OP is not aware of that. One can use the following function (taken from here) to remove these
'Add a reference to Microsoft VBScript Regular Expressions 5.5
Public Function RemoveWhiteSpace(ByVal target As String) As String
With New RegExp
.Pattern = "\s"
.MultiLine = True
.Global = True
RemoveWhiteSpace = .Replace(target, vbNullString)
End With
End Function
This will remove also white spaces from textline even if you want to keep them as the empty spaces in the line of your picture.
Update 2: In order to avoid invalid filename add the following function
Function isValidFilename(ByVal FileName As String) As Boolean
'PURPOSE: Determine If A Given Excel File Name Is Valid
'SOURCE: www.TheSpreadsheetGuru.com/the-code-vault
With New RegExp
.Pattern = "[\\/:\*\?""<>\|\[\]]"
ValidFileName = Not .Test(FileName)
End With
End Function
I modified the main procedure.

VBA Getting run-time error (5) when trying to Delete existing file

I am getting a run-time error invalid procedure (error # 5) on this line:
afiles(countoflines).Delete True
I can't understand why. The save copy to the path works fine, and the assignation of the FSO to the folder files works , but I can't delete the 'x'th indexed item in the folder. Can someone assist with this?
Thanks
Option Explicit
Private Sub Workbook_Open()
Dim aFSO As New Scripting.FileSystemObject
Dim aFolder As Object
Dim aFiles As Object
Set aFolder = aFSO.GetFolder("R:\Groups\Finance\Ops Finance\Reporting\F18 Cost Analysis\Standard Costing\Std Cost Variances\Variance Master Back-Ups\")
If aFolder Is Nothing Then MsgBox "Directory not found!", vbExclamation: Exit Sub
Set aFiles = aFolder.Files
Application.StatusBar = "Saving back up copy"
ThisWorkbook.SaveCopyAs aFolder.Path & "\" & _
VBA.Replace(ThisWorkbook.Name, ".xlsm", "") & "_copy_" & _
VBA.Format$(Now, "m-d-yyyy hhmmss AM/PM") & ".xlsm"
Call CleanUpArchive(aFolder, aFolder.Path & Chr(92), aFiles.Count)
Set aFolder = Nothing
Set aFSO = Nothing
End Sub
'Cleans up archive file by deleting the 11th file (oldest copy)
Private Function CleanUpArchive(Folder As Object, Path As String, _
CountofFiles As Integer)
Dim aFiles As Scripting.Files
Set aFiles = Folder.Files
If CountofFiles > 10 Then
aFiles(CountofFiles).Delete True
End If
Set aFiles = Nothing
End Function
Untested, written on mobile. The below is not that different from your code, but might do what you need.
Option Explicit
Private Sub Workbook_Open()
Dim folderPath as string
folderPath = dir$("R:\Groups\Finance\Ops Finance\Reporting\F18 Cost Analysis\Standard Costing\Std Cost Variances\Variance Master Back-Ups\", vbdirectory)
If Len(folderPath) = 0 then
Msgbox("Could not locate folder")
Exit sub
Elseif strcomp(right(folderPath, 1),"\", vbbinarycompare) <> 0 then ' this might be unnecessary, depends if dir() on vbdirectory returns \ at end or not, cannot remember or test'
folderPath = folderPath & "\"
End if
Dim filenames() as string
Redim filenames(1 to 2, 1 to 1000) ' 1000 = magic number, change if needed.'
Dim fileIndex as long
Dim filename as string
Filename = dir$(folderPath & "*")
Do until Len(filename) = 0
Fileindex = fileindex +1
Filename(1, fileindex) = folderPath & filename
Filenames(2, fileindex) = filedatetime(Filename(1, fileindex))
Filename = dir$()
Loop
Redim preserve filenames(1 to 2, 1 to fileindex)
ThisWorkbook.SaveCopyAs folderPath & _
VBA.Replace(ThisWorkbook.Name, ".xlsm", "_copy_" & _
VBA.Format$(Now, "m-d-yyyy hhmmss AM/PM") & ".xlsm")
Dim Oldest as Date
Dim OldestIndex as long
Oldest = filenames(2,1) ' Initialise with first value'
' Might be better to store below in dictionary -- or any key-value/associative structure. But should work nonetheless.'
For fileindex = lbound(filenames,2) to ubound(filenames,2)
If filenames(2, fileindex) < oldest then
Oldest = filenames(2, fileindex)
OldestIndex = fileindex
End if
Next fileindex
Dim fileIsOpen as Boolean
On error resume next
Open filenames(1, OldestIndex) For Input Lock Read As #1
fileIsOpen = err.number <> 0
On error goto 0
Close #1
If fileIsOpen then
msgbox("Attempted to delete file at:" & filenames(1, OldestIndex) & " but file may be open elsewhere or by another user.")
Exit sub
Else
' In theory, the file could go from not-in-use to in-use between the check above and the delete below. Might be better just to try to kill without checking but with on error resume, and then checking if file still exists or is open.'
Kill filenames(1, OldestIndex)
End if
End sub

Error code 52 when counting the number of rows in a text file

I am writing a code to open a text file through VBA, and match the value of mail_id taken from excel in the notepad. I saw few sample codes for this, and have modified it according to my data and requirement, but when counting the number of lines of the code, it is displaying error number 52. Also, please let me know how can I search for a string in notepad, and copy the text below it, if the string matches. Please find below code for reference:
If mail_id <> " " Then
Dim fso As Object
Dim sfile As String
Set fso = CreateObject("shell.application")
sfile = "C:\My Documents\Textfile.txt"
fso.Open (sfile)
f = FreeFile
Do While Not EOF(f)
Ingline = Ingline + 1
Line Input #f, strLine
If InStr(1, strLine, mail_id, vbBinaryCompare) > 0 Then
MsgBox Ingline, vbInformation
bInfound = True
End If
Exit Do
'End If
Loop
Close #f
If Not blnFound Then
MsgBox "Search string not found", vbInformation
End If
Its showing the error at Do While Not EOF(f)
I think you mixed up different methods how to open text files in VBA:
This should open a file and read line by line:
Option Explicit
Public Sub ReadLineByLineFromTextFile()
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Dim FileName As String
FileName = "c:\temp\test.txt"
Dim MyFile As Object
Set MyFile = fso.OpenTextFile(FileName, ForReading)
'' Read from the file line by line
Do While MyFile.AtEndOfStream <> True
Dim TextLine As String
TextLine = MyFile.ReadLine
'' Do stuff to TextLine here like …
'If InStr(1, TextLine, mail_id, vbBinaryCompare) > 0 Then
' MsgBox "found", vbInformation
'
' Exit Do 'cancel loop when found
'End If
Loop
MyFile.Close
End Sub
You are mixing up two different ways to read a text file in VBA. Both are explained in the same question on SO.
a) Use open: https://stackoverflow.com/a/11528932/7599798
b) Use FileSystemObject: https://stackoverflow.com/a/11529980/7599798

Reading a specific line from multiple text files within several folders

I have a large number of text files within several folders and I need the 14th line from each text file, I was wondering if there was anyway to do that?
Currently I have the following script setup, where I input the folder directory into cell A19 within the first worksheet and this returns the file paths of all files within the directory. I then want to get the information from the 14th line of every text file, utilising the aforementioned file paths. This is my
code so far:
Private Sub CommandButton1_Click()
'Call the recursive function
ListAllFiles ThisWorkbook.Sheets(1).Range("A19").Value, ThisWorkbook.Sheets(2).Cells(1, 1)
ReadTxtFiles
MsgBox "Task Completed"
End Sub
Private Sub ListAllFiles(root As String, targetCell As Range)
Dim objFSO As Object, objFolder As Object, objSubfolder As Object, objFile As Object
Dim i As Integer, Target_Path As String
'Create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Get the folder object
Set objFolder = objFSO.GetFolder(root)
'loops through each file in the directory and prints their names and path
For Each objFile In objFolder.Files
'print file name
targetCell.Value = objFile.Name
'print file path
targetCell.Offset(, 1).Value = objFile.Path
'print file type
'targetCell.Offset(, 2).Value = objFile.Type
'print file date created
'targetCell.Offset(, 3).Value = objFile.DateCreated
'print file date last accessed
'targetCell.Offset(, 4).Value = objFile.DateLastAccessed
'print file date last modified
'targetCell.Offset(, 5).Value = objFile.DateLastModified
Set targetCell = targetCell.Offset(1)
Next objFile
' Recursively call the function for subfolders
For Each objSubfolder In objFolder.SubFolders
ListAllFiles objSubfolder.Path, targetCell
Next objSubfolder
End Sub
Private Sub ReadTxtFiles()
'Dim start As Date
'start = Now
Dim oFSO As Object
Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim oFS As Object
'''''Assign the Workbook File Name along with its Path
'''''Change path of the Target File name
Dim v As Variant, filepath As String
For Each v In Worksheets("Sheet2").Columns("B").SpecialCells(xlCellTypeConstants)
filepath = v.Value
Debug.Print filepath
Dim arr(100000) 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
This is where I get stuck. I would like to read each text file and get the 14th line of each and nothing more.
Your ReadTxtFiles subroutine seems to read the data in, and then doesn't do anything with it. Maybe it does something in the part of the code you didn't post.
However, it is relatively straight-forward to just read 14 lines, and then whatever was last read in is the record you want:
Private Sub ReadTxtFiles()
'Dim start As Date
'start = Now
Dim oFSO As Object
Set oFSO = CreateObject("Scripting.FileSystemObject")
Dim oFS As Object
'''''Assign the Workbook File Name along with its Path
'''''Change path of the Target File name
Dim v As Variant, filepath As String
For Each v In Worksheets("Sheet2").Columns("B").SpecialCells(xlCellTypeConstants)
filepath = v.Value
Debug.Print filepath
Dim rec As String
Dim i As Long
i = 0
rec = ""
If oFSO.FileExists(filepath) Then
On Error GoTo Err
Set oFS = oFSO.OpenTextFile(filepath)
Do While Not oFS.AtEndOfStream
rec = oFS.ReadLine
i = i + 1
If i = 14 Then Exit Do
Loop
oFS.Close
Else
MsgBox "The file path is invalid.", vbCritical, vbNullString
Exit Sub
End If
'Check we read 14 records
If i < 14 Then
MsgBox "Not enough records"
Exit Sub
End If
'do whatever you want with "rec"
'...
'...
Does this help? To test, run the procedure TestGetLine after setting path and file name.
Private Sub TestGetLine()
' 12 Apr 2017
Dim Pn As String ' Path
Dim Fn As String ' File
Dim Ffn As String
Pn = "D:\My Documents\"
Fn = "TextFile 14"
Ffn = Pn & Fn & ".txt"
If Len(Dir(Ffn)) Then
Debug.Print TextLine(Ffn, 14)
Else
MsgBox Chr(34) & Fn & """ doesn't exist.", _
vbInformation, "Invalid file name"
End If
End Sub
Private Function TextLine(ByVal Ffn As String, _
LineNum As Integer) As String
' 12 Apr 2017
Dim FileNum As Integer
Dim Txt As String
Dim Ln As Integer
Close ' close any open text files
FileNum = FreeFile
Open Ffn For Input As #FileNum
Do While Not EOF(1) ' Loop until end of file.
Line Input #1, Txt
Ln = Ln + 1
If Ln = LineNum Then Exit Do
Loop
If Ln < LineNum Then
Txt = "File """ & Split(Ffn, "\")(UBound(Split(Ffn, "\"))) & _
""" has only " & Ln & " lines. No line was copied"
End If
Close
TextLine = Txt
End Function
You can feed path (Pn) and file name (Fn) in which ever loop you require. Let the code add the extension .txt. Specify which line number you want in the function call, like TextLine(Ffn, 14) which specifies line 14.
It's been a long time since I've done VBA but to find the nth iteration of a thing, use MOD. This is explains how to use it and there are plenty of other examples you can find on line.