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.
Related
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
I am trying to edit code that someone else wrote. I have done NO VBA and very little coding in general.
The original code is written for a 5 digit number and we now have files that are six digits. I have tried to copy the code but change it to 6 digit numbers below the current code above Next objFile at the end. This has not worked.
The main issue here is I didn't write the original code and I don't understand the logic. I have tried just changing all of the 5's to 6's and the 99999 to 999999. I have tried copying from Folder = "" down, changing them to 6 digits and pasting below Next objFile. This didn't work either.
Sub CopyPics()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim varDirectory As Variant
Dim objSubFolder As Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(Application.ActiveWorkbook.Path)
Dim Dest As String
Dest = "R:\Field Assurance\FA PHOTOS AND INFORMATION\"
'Loop through each file in this folder
For Each objFile In objFolder.Files
Folder = "" 'Empty old folder name
MainFolder = "" 'Empty old folder name
For i = 1 To Len(objFile.Name)
Test = Mid(objFile.Name, i, 5)
If Test >= 10000 And Test <= 99999 Then 'For files: Find any five numbers in a row and assume it to be the file number.
Folder = "NC-" & Mid(objFile.Name, i, 5) 'If found, create new folder.
i = Len(objFile.Name) 'In other words, take the first 5 numbers, then get out.
End If
Next
For Each objSubFolder In objFolder.subfolders 'Find the main folder.
If Right(Folder, 5) >= Mid(objSubFolder.Name, 4, 5) And Right(Folder, 5) <= Mid(objSubFolder.Name, 18, 5) Then 'If my file number is within the main folder bounds...
MainFolder = objSubFolder.Name & "\" 'Use that folder.
End If
Next objSubFolder
If Len(Folder) = 8 And Len(MainFolder) = 23 Then 'If real folders are identified...
On Error Resume Next
If Dir(Dest & MainFolder & Folder) = "" Then 'Check to see if the directory/folder does not exist...
objFSO.CreateFolder (Dest & MainFolder & Folder) 'If not, make one.
End If
'Rename that file's directory to be the new one - aka cut and paste file into new folder.
Name Application.ActiveWorkbook.Path & "\" & objFile.Name As Dest & MainFolder & Folder & "\" & objFile.Name
End If
Next objFile
ActiveWorkbook.Close
End Sub
This is a bit more complex than your original code but I think it's more robust...
Lightly tested.
Option Explicit
Sub CopyPics()
'use constants for fixed values
Const DEST As String = "R:\Field Assurance\FA PHOTOS AND INFORMATION\"
Dim objFSO As Object, srcFolder As Object, objFile As Object
Dim objSubFolder As Object, destFolder As Object, fNum, folderName, picFolderName
Dim FileWasMoved As Boolean, sMsg
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set srcFolder = objFSO.GetFolder(Application.ActiveWorkbook.Path) 'ThisWorkbook.Path ?
Set destFolder = objFSO.GetFolder(DEST) 'parent destination folder
'Loop through each file in this folder
For Each objFile In srcFolder.Files
FileWasMoved = False 'reset "moved" flag
fNum = ExtractNumber(objFile.Name) 'get the file number
If Len(fNum) > 0 Then 'any number found?
folderName = "NC-" & fNum
For Each objSubFolder In destFolder.subfolders 'Find the subfolder.
If IsTheCorrectFolder(objSubFolder.Name, fNum) Then
picFolderName = objSubFolder.Path & "\" & folderName
If Not objFSO.folderexists(picFolderName) Then
objFSO.CreateFolder picFolderName
End If
'move the file
Name objFile.Path As picFolderName & "\" & objFile.Name
FileWasMoved = True 'flag file as moved
Exit For
End If
Next objSubFolder
End If 'filename contains a number
'if file was not moved then add it to the list....
If Not FileWasMoved Then sMsg = sMsg & vbLf & objFile.Name
Next objFile
'warn user if some files were not moved
If Len(sMsg) > 0 Then
MsgBox "Some files were not moved:" & vbLf & sMsg, vbExclamation
End If
End Sub
'Return true/false depending on whether this is the correct
' folder to hold the specified filenumber
Function IsTheCorrectFolder(folderName, fileNumber) As Boolean
Dim arr, num1, num2, rv As Boolean
rv = False 'default return value
arr = Split(folderName, "thru") 'split folder name on "thru"
If UBound(arr) = 1 Then 'should have two parts
'get the numbers from each part and compare against the file number
num1 = ExtractNumber(arr(0))
num2 = ExtractNumber(arr(1))
If Len(num1) > 0 And Len(num2) > 0 Then
fileNumber = CLng(fileNumber) 'convenrt to Long for comparison
rv = (fileNumber >= CLng(num1) And fileNumber <= CLng(num2))
End If
End If
IsTheCorrectFolder = rv
End Function
'Extract the first 5- or 6-digit number from a string
' Match is "greedy" so if there are six digits it will match 6 and
' not just the first 5...
Function ExtractNumber(txt)
Dim re As Object, allMatches, rv
Set re = CreateObject("VBScript.RegExp")
re.Pattern = "(\d{5,6})"
re.ignorecase = True
re.Global = True
Set allMatches = re.Execute(txt)
If allMatches.Count > 0 Then rv = allMatches(0) 'if there's a match then return the first one
ExtractNumber = rv
End Function
You need to change the lower limit in IF condition also. Like
If Test >= 10000 And Test <= 99999 Then
becomes
If Test >= 100000 And Test <= 999999 Then
Currently the loop could be exiting when it finds the first five digit number.
I have a folder "test" containing several dbf files. I would like vba to open them in excel file and save them (in excel format) in another folder keeping the same dbf file names.
I found this code on the net and am trying to use this code for my needs but it won't work. Error message:
"sub of function not defined"
...please look into it.
Sub test()
Dim YourDirectory As String
Dim YourFileType As String
Dim LoadDirFileList As Variant
Dim ActiveFile As String
Dim FileCounter As Integer
Dim NewWb As Workbook
YourDirectory = "c:\Users\navin\Desktop\test\"
YourFileType = "dbf"
LoadDirFileList = GetFileList(YourDirectory)
If IsArray(LoadDirFileList) = False Then
MsgBox "No files found"
Exit Sub
Else
' Loop around each file in your directory
For FileCounter = LBound(LoadDirFileList) To UBound(LoadDirFileList)
ActiveFile = LoadDirFileList(FileCounter)
Debug.Print ActiveFile
If Right(ActiveFile, 3) = YourFileType Then
Set NewWb = Application.Workbooks.Open(YourDirectory & ActiveFile)
Call YourMacro(NewWb)
NewWb.SaveAs YourDirectory & Left(ActiveFile, Len(ActiveFile) - 4) & ".xlsx"
NewWb.Saved = True
NewWb.Close
Set NewWb = Nothing
End If
Next FileCounter
End If
End Sub
You missing the functions GetFileList and YourMacro. A quick search brought me to this website (I think you copied it from there). http://www.ozgrid.com/forum/printthread.php?t=56393
There are the missing functions. Copy those two also in your modul to make it run (I tested it with pdf-Files):
Function GetFileList(FileSpec As String) As Variant
' Author : Carl Mackinder (From JWalk)
' Last Update : 25/05/06
' Returns an array of filenames that match FileSpec
' If no matching files are found, it returns False
Dim FileArray() As Variant
Dim FileCount As Integer
Dim FileName As String
On Error GoTo NoFilesFound
FileCount = 0
FileName = Dir(FileSpec)
If FileName = "" Then GoTo NoFilesFound
' Loop until no more matching files are found
Do While FileName <> ""
FileCount = FileCount + 1
ReDim Preserve FileArray(1 To FileCount)
FileArray(FileCount) = FileName
FileName = Dir()
Loop
GetFileList = FileArray
Exit Function
NoFilesFound:
GetFileList = False
End Function
Sub YourMacro(Wb As Workbook)
Dim ws As Worksheet
Set ws = Wb.Worksheets(1)
ws.Range("A6").Value = "=((+A2*$CN2)+(A3*$CN3)+(A4*$CN4)+(A5*$CN5))/SUM($CN2:$CN5)"
ws.Range("A6").Copy ws.Range("B6:CM6")
ws.Range("CO6").Value = "=CO2"
End Sub
To save files in a different directory:
Dim SaveDirectory As String
SaveDirectory = "c:\Users\navin\Desktop\test\converted to excel"
Replace this line
NewWb.SaveAs YourDirectory & Left(ActiveFile, Len(ActiveFile) - 4) & ".xlsx"
with this
NewWb.SaveAs SaveDirectory & Left(ActiveFile, Len(ActiveFile) - 4) & ".xlsx"
I have two codes. One will search and name every folder within a directory. The other will list the files and file names within a single folder. I am not proficient enough with VBA to figure this out, so I need StackOverflow!
Here is the File Name Listing program:
Sub Example1()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim i As Integer
'Create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Get the folder object
Set objFolder = objFSO.GetFolder("\\fc8fsp01\litho_recipe_amat_data")
i = 1
'loops through each file in the directory and prints their names and path
For Each objFile In objFolder.Files
'print file name
Cells(i + 1, 1) = objFile.Name
'print file path
Cells(i + 1, 2) = objFile.Path
i = i + 1
Next objFile
End Sub
Here is the second code that will navigate sub-folders to write folder names:
Option Explicit
Dim i As Long, j As Long
Dim searchfolders As Variant
Dim FileSystemObject
Sub ListOfFolders()
Dim LookInTheFolder As String
i = 1
LookInTheFolder = "\D: ' As you know; you should modificate this row.
Set FileSystemObject = CreateObject("Scripting.FileSystemObject")
For Each searchfolders In FileSystemObject.GetFolder(LookInTheFolder).SubFolders
Cells(i, 1) = searchfolders
i = i + 1
SearchWithin searchfolders
Next searchfolders
End Sub
Sub SearchWithin(searchfolders)
On Error GoTo exits
For Each searchfolders In FileSystemObject.GetFolder(searchfolders).SubFolders
j = UBound(Split(searchfolders, "\"))
Cells(i, j) = searchfolders
i = i + 1
SearchWithin searchfolders
Next searchfolders
exits:
End Sub
I need a code that will search all sub folders and list all files contained. Please help D:
Because of speed issues when some of the folders I was accessing were present on a network drive, I wrote a little VBA program that uses the Windows Shell dir command. With the proper arguments, this will return all the files in the base directory; as well as all the subfolders and files and so forth. I have it write the results to a text file, which I then read into Excel for further processing.
Compared with using VBA's DIR or the FSO, this ran about five times faster when the files were on a network drive -- not so noticeable when on the local computer -- but I present it as another approach.
You must set a reference to Windows Script Host Object Model.
sDrive and sBasePath are used to set the starting folder name.
sFileList is where the results will be written into a text file.
The /S argument Displays files in specified directory and all subdirectories.
The /B argument results in omitting heading information and summary
If you run CMD.EXE and look for help on the dir command, you will see an explanation of the other arguments.
Public sDrive As String
Public sBasePath As String
Public Const sFileList As String = "C:\Users\Ron\FileList.txt"
Option Explicit
Sub GetDirTree()
Dim WSH As WshShell
Dim lErrCode As Long
Set WSH = New WshShell
lErrCode = WSH.Run("cmd.exe /c dir """ & sDrive & sBasePath & """/B /S >" & sFileList, 0, True)
If lErrCode <> 0 Then
MsgBox ("Error in GetDirTree: Error Number: " & CStr(lErrCode))
Stop
End If
End Sub
This is the function I use to find all files in a directory.
Public Function RecursiveDir(colFiles As Collection, _
ByVal strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
strTemp = Dir(strFolder & strFileSpec)
Do While strTemp <> vbNullString
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
'Fill colFolders with list of subdirectories of strFolder
If bIncludeSubfolders Then
strTemp = Dir(strFolder, vbDirectory)
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
'Garbage collection
Set colFolders = Nothing
End Function
This function will populate a collection of every file name in a given directory. And if you want you can set the bIncludeSubfolders to True, and it will recursively search all subfolders within this directory. To use this function, you need the following:
Dim colFiles As New Collection ' The collection of files
Dim Path As String ' The parent Directory you want to search
Dim subFold As Boolean ' Search sub folders, yes or no?
Dim FileExt As String ' File extension type to search for
Then just set FileExt = "*.*" Which will find every file with every file extension. Hopefully this helps a little more.
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