I have about 200ish workbooks That I need opened in a dir containing thousands of files. The name of these 200 files have been placed in Sheet 4 B3:B231.
Whenever I run the macro however, I only get the code to work on 4 of these files, 1 of the 4 isn't even mentioned in the specified range.
Dim wb As Workbook, shtDest As Worksheet
Dim MyFile As String
Dim Filepath As String
Dim WoBo As Variant
Dim DirArray As Variant
DirArray = Sheets("Sheet4").Range("B3:B231").Value
Filepath = "C:\"
MyFile = Dir(Filepath)
Set shtDest = ThisWorkbook.Sheets("Sheet1")
For Each WoBo In DirArray
'code
Next WoBo
End Sub
I'm quite new to arrays and I don't really understand the For Each line, so I suspect the error to be there.
Try something like the following. A For Loop is faster than a For Each for arrays and using Transpose turns it into a one dimensional array to loop over.
Dim dirArray()
dirArray = Application.Transpose(ThisWorkbook.Worksheets("Sheet4").Range("B3:B231").Value)
For i = LBound(dirArray) To Ubound(dirArray)
If instr(dirArray(i),"xls") > 0 Then '<== very basic check
Workbooks.Open(dirArray(i)) '<== should be full filepath of file
' Do stuff
End If
Next
Related
I have a list of files with the same structure and I want to extract some information from columns A, B, and C and print it to another workbook.
I found a way to do it for a single file, but now I don't understand how can I do it using the list of given files. I tried using collections, but it doesn't work.
Here's what I came up with:
Sub Pulsante1_Click()
Dim FileGeStar As Variant
Dim myCol As Collection
Set myCol = New Collection
myCol.Add "C:\Users\xxx\Desktop\articoli_def.xlsx"
myCol.Add "C:\Users\xxx\Desktop\pippo\SS20_def_ENG.xlsx"
For Each FileGeStar In myCol
Workbooks.Open Filename:=FileGeStar
FileGeStar = Application.ActiveWorkbook.Name
Dim Code As String
Dim Description As String
Dim FilePath As String
Dim i As Long
i = 2
While Range("A" & i) <> ""
FilePath = Application.ActiveWorkbook.Path
Code = Trim(Range("A" & i).Value)
Description = Trim(Range("B" & i).Value)
Workbooks("Report.xlsm").Worksheets(1).Range("A" & i).Value = FilePath
Workbooks("Report.xlsm").Worksheets(1).Range("B" & i).Value = Code
Workbooks("Report.xlsm").Worksheets(1).Range("C" & i).Value = Description
i = i + 1
Wend
Next FileGeStar
End Sub
What can I do?
This might look like an overkill, but I hope the code and comment's are self explanatory:
Option Explicit
Sub Pulsante1_Click()
Dim DestinationWorkbook As Workbook
Set DestinationWorkbook = ThisWorkbook 'I think report.xlsm is the workbook running the code
'if report.xlsm is not the workbook running the code then change thisworkbook for workbooks("Report.xlsm")
'add as many paths as you need to, another way would be to write them in a sheet and loop through to fill the array
Dim MyPaths As Variant
MyPaths = Array("C:\Users\xxx\Desktop\articoli_def.xlsx", "C:\Users\xxx\Desktop\pippo\SS20_def_ENG.xlsx")
'Declare a workbook variable for the source workbooks
Dim SourceWorkbook As Workbook
'Declare a long variable to loop through your path's array
Dim i As Long
'loop through the start to the end of your array (will increase as the array does)
For i = LBound(MyPaths) To UBound(MyPaths)
Set SourceWorkbook = OpenWorkbook(MyPaths(i)) 'this will set the workbook variable and open it
CopyData SourceWorkbook, DestinationWorkbook 'this will copy the data to your destination workbook
SourceWorkbook.Close , False
Set SourceWorkbook = Nothing
Next i
End Sub
Private Function OpenWorkbook(FullPath As String) As Workbook
Set OpenWorkbook = Workbooks.Open(FullPath, False, True)
End Function
Private Sub CopyData(wbO As Workbook, wbD As Workbook)
'this procedure calculates the last row of your source workbook and loops through all it's data
'later calls the AddDataToMasterWorkbook procedure to paste the data
With wbO.Sheets(1) 'Im assuming your source workbook has the data on sheet1
Dim LastRow As Long
LastRow = .Cells(Rows.Count, "A").End(xlUp).Row
Dim FilePath As String
FilePath = wbO.Path
Dim Code As String
Dim Description As String
Dim C As Range
For Each C In .Range("A2:A" & LastRow) 'this will loop from A2 to the last row with data
Code = Trim(C)
Description = Trim(C.Offset(, 1))
AddDataToMasterWorkbook wbD, FilePath, Code, Description
Next C
End With
End Sub
Private Sub AddDataToMasterWorkbook(wb As Workbook, FilePath As String, Code As String, Description As String)
'This procedure calculates the last row without data and adds the items you need every time
With wb.Sheets(1)
Dim LastRow As Long
LastRow = .Cells(Rows.Count, "A").End(xlUp).Row + 1
.Range("A" & LastRow) = FilePath
.Range("B" & LastRow) = Code
.Range("C" & LastRow) = Description
End With
End Sub
To loop though files, you can indeed use a collection, or an array, you can also loop through all files in directory with certain extension, or partial file name. Check out DIR function.
Best not to use ActiveWorkbook, better approach would be to set a workbook object like so: Set wb = Workbooks.Open(fullPathToYourWorkbook).
For what you're doing, there's no need to go row by row, much more efficient way would be to copy entire range, not only it's a lot quicker but also it's only 1 line of code; assuming your destination is ThisWorkbook.Sheets(1) and wb object is set: wb.Range("A:C").Copy Destination:=Thisworkbook.Sheets(1).Range("A:C"). If you need to edit copied data (trim or whatever) consider Range Replace method.
However, if you want to go row by row for whatever reason, as BigBen mentioned in the comment - get rid of While loop.
It's a good idea to set Application.ScreenUpdating to False when opening/closing workbooks, then back to True once it's all done. It will prevent user from accidentaly clicking something etc and will make it look like it's not opening any workbook.
Here's my approach (untested) assuming the workbook you want to copy data to is Workbooks("Report.xlsm").Worksheets(1):
Sub Pulsante1_Click()
'set workbook object for the destination workbook
set wb_dest = Workbooks("Report.xlsm").Worksheets(1)
'disable screen updating
Application.ScreenUpdating = False
For Each target_wb In Array("C:\Users\xxx\Desktop\articoli_def.xlsx", "C:\Users\xxx\Desktop\pippo\SS20_def_ENG.xlsx")
'set wb object and open workbook
Set wb = Workbooks.Open(target_wb)
'find last row in this workbooks in columns A:B (whichever is greater)
LastRow = wb.Range("A:B").Find(What:="*", After:=wb.Range("A1"), SearchDirection:=xlPrevious).row
'copy required data
wb.Range("A1:B" & LastRow).Copy Destination:=wb_dest.Range("B1:C" & LastRow)
'fill column A with path to the file
wb_dest.Range("A1:A" & LastRow).Value = wb.Path
'close workbook
wb.Close False
Next
'enable screen updating
Application.ScreenUpdating = True
End Sub
Obviously an array is not the best approach if you have loads of different files, collection would be a lot clearer to read and edit in the future, unless you want to create a dynamic array, but there's no need for that in my opinion. I didn't declare variables or write any error handling, it's a simple code just to point you in the right direction.
If you want to disable workbook events or/and alerts, you can set Application.DisplayAlerts and Application.EnableEvents to False temporarily.
I have a macro that is to be used inside a macro I found on internet.
The second macro runs through all Excel files inside a folder:
Sub RunOnAllFilesInFolder()
Dim folderName As String, eApp As Excel.Application, fileName As String
Dim wb As Workbook, ws As Worksheet, currWs As Worksheet, currWb As Workbook
Dim fDialog As Object: Set fDialog = Application.FileDialog(msoFileDialogFolderPicker)
Set currWb = ActiveWorkbook: Set currWs = ActiveSheet
'Select folder in which all files are stored
fDialog.Title = "Select a folder"
fDialog.InitialFileName = currWb.Path
If fDialog.Show = -1 Then
folderName = fDialog.SelectedItems(1)
End If
'Create a separate Excel process that is invisibile
Set eApp = New Excel.Application: eApp.Visible = False
'Search for all files in folder [replace *.* with your pattern e.g. *.xlsx]
fileName = Dir(folderName & "\*.*")
Do While fileName <> ""
'Update status bar to indicate progress
Application.StatusBar = "Processing " & folderName & "\" & fileName
Set wb = eApp.Workbooks.Open(folderName & "\" & fileName)
'...
'YOUR CODE HERE
'...
wb.Close SaveChanges:=False 'Close opened worbook w/o saving, change as needed
Debug.Print "Processed " & folderName & "\" & fileName
fileName = Dir()
Loop
eApp.Quit
Set eApp = Nothing
'Clear statusbar and notify of macro completion
Application.StatusBar = ""
MsgBox "Completed executing macro on all workbooks"
End Sub
I made a macro that, based on three named cells in a file, finds the ranges and change the style of some other ranges.
Not all Excel files have all three named cells, so I need the code to work when the range is not valid.
I tried to use error handlers but I received the following error:
"Loop without Do"
I tried IF and else for when the range does not exist and also found errors.
My code:
Dim test As Worksheet
Dim rOutstandingR As Range
Dim rAdditionalDueR As Range
Dim rFollowingR As Range
Dim rOutstandingBorderR As Range
Dim rAdditionalDueBorderR As Range
Dim rFollowingBorderR As Range
Dim ORow As Long
Dim OCol As Long
Dim ARow As Long
Dim ACol As Long
Dim FRow As Long
Dim FCol As Long
Dim OutstandingTopBorderRange As Range
Dim OutstandingBottomBorderRange As Range
Dim OutstandingRightBorderRange As Range
Dim AdditionalDueTopBorderRange As Range
Dim AdditionalDueBottomRange As Range
Dim AdditinalDueRightBorderRange As Range
Dim FollowingTopBorderRange As Range
Dim FollowingBottomBorderRange As Range
Dim FollowingRightBorderRange As Range
Dim OutstandingTextRange As Range
Dim AdditionalDueTextRange As Range
Dim FollowingTextRange
With Range("A1:Z999")
'Setting up another range that may not exists within excel file and give an error
Set rOutstandingR = ActiveSheet.Range("Outstanding")
rOutstandingBorderR = rOutstandingR.Address
rOutstandingR.Select
‘more code in which I change format of cells based on range
'Setting up another range that may not exists within excel file and give an error
Set rAdditionalDueR = ActiveSheet.Range("AdditionalDue")
rAdditionalDueBorderR = rAdditionalDueR.Address
rAdditionalDueR.Select
‘more code in which I change format of cells based on range
'Setting up another range that may not exists within excel file and give an error
'Setting Up rFollowingR as Range for Following Variable
Set rFollowingR = ActiveSheet.Range("Following")
rFollowingBorderR = rFollowingR.Address
rFollowingR.Select
‘more code in which I change format of cells based on range
As you can imagine by the amount of ranges, there is a lot of code in between but it's only based on the three major ranges for the named cells "Outstanding", "AdditionalDue" and "Following".
I need that all the codes between ranges work and if the first range doesn't exist goes to validate then next and do the changes of format, etcetera.
I tried to put some error handlers (resume labels) but I wasn't able to fix it when I used the code above within the first macro due to the loop through all the files.
How can I put the error handlers so I could use this macro inside the one that runs over a folder of files.
There are two ways to handle this, however with the snippets provided it's not straightforward to test what you're working on. You may want to consider separating your code into multiple subs/functions.
This solution should be clean assuming that you want some type of handling code to run:
With range("A1:Z999")
'Setting up another range that may not exists within excel file and give an error
On Error GoTo OutstandingError
Set rOutstandingR = ActiveSheet.range("Outstanding")
rOutstandingBorderR = rOutstandingR.Address
rOutstandingR.Select
OutstandingResume:
'more code in which I change format of cells based on range
'Setting up another range that may not exists within excel file and give an error
On Error GoTo AdditionalDueError
Set rAdditionalDueR = ActiveSheet.range("AdditionalDue")
rAdditionalDueBorderR = rAdditionalDueR.Address
rAdditionalDueR.Select
AdditionalDueResume:
'more code in which I change format of cells based on range
'Setting up another range that may not exists within excel file and give an error
'Setting Up rFollowingR as Range for Following Variable
On Error GoTo FollowingError
Set rFollowingR = ActiveSheet.range("Following")
rFollowingBorderR = rFollowingR.Address
rFollowingR.Select
FollowingResume:
'more code in which I change format of cells based on range
GoTo Complete
OutstandingError:
'Error handling code here
Resume OutstandingResume
AdditionalDueError:
'Error handling code here
Resume AdditionalDueResume
FollowingError:
'Error handling code here
Resume FollowingResume
Complete:
This solution just bypasses the block entirely without any handling code:
With range("A1:Z999")
'Setting up another range that may not exists within excel file and give an error
On Error GoTo OutstandingResume
Set rOutstandingR = ActiveSheet.range("Outstanding")
rOutstandingBorderR = rOutstandingR.Address
rOutstandingR.Select
OutstandingResume:
'more code in which I change format of cells based on range
'Setting up another range that may not exists within excel file and give an error
On Error GoTo AdditionalDueResume
Set rAdditionalDueR = ActiveSheet.range("AdditionalDue")
rAdditionalDueBorderR = rAdditionalDueR.Address
rAdditionalDueR.Select
AdditionalDueResume:
'more code in which I change format of cells based on range
'Setting up another range that may not exists within excel file and give an error
'Setting Up rFollowingR as Range for Following Variable
On Error GoTo FollowingResume
Set rFollowingR = ActiveSheet.range("Following")
rFollowingBorderR = rFollowingR.Address
rFollowingR.Select
FollowingResume:
'more code in which I change format of cells based on range
If you'd like to go in a different direction, here is a function that returns a boolean for whether or not a named range exists. Using this you could refactor this to use conditionals instead of relying on error checking and line jumps.
Private Function BET_RangeNameExists(nname) As Boolean
Dim n As Name
BET_RangeNameExists = False
For Each n In ActiveWorkbook.Names
If UCase(n.Name) = UCase(nname) Then
BET_RangeNameExists = True
Exit Function
End If
Next n
End Function
Taken from https://bettersolutions.com/excel/named-ranges/vba-named-range-exists.htm
I have a user-form that pastes folder-paths into a list. I then have the code below that is supposed to loop through that list and list all the sub-folders (then I'll probably have another code loop through the sub-folders to get the excel workbooks).
I know it's inelegant, because ultimately what I want is have my list of paths be looked in one a time, through each folder and subfolder to find and list the excel files. But there was a question like that and it was taken down. The question was then referred to a different q&a that I did not understand, that had to do with individual FILE NAMES, typed in a single cell not a range, nor as a path. I speak Russian, which some of his code was in, and still couldn't quite understand what his code meant and was referring to, and when I tried it, it kept telling met that "GetData" was undefined? so I've tried to ask a different but similar question in the hope that someone can explain to me what I need to do, as I've gone as far as I can and have tried to adapt both codes from the links in this post as well as many others. I have several modules with broken code that doesn't work, and the closest I've come is the code below. At this point I'd settle simply for a way to list the excel file names from a list of paths.
Option Explicit
Dim i As Long, j As Long
Dim searchfolders As Variant
Dim FileSystemObject
Sub ListOfFolders77()
Dim LookInTheFolder As String
'Dim ws As Worksheet: Set ws = Sheets("Output4")
Dim ws2 As Worksheet: Set ws2 = Sheets("Output3")
Dim rng As Range: Set rng = ws2.Range("A1:A" & Rows.Count).End(xlUp)
Dim mypath As Range
'Dim Region As Range: Set Region = ws.Range("A2")
'Dim district As Range: Set district = ws.Range("B2")
'Dim city As Range: Set city = ws.Range("C2")
'Dim atlas As Range: Set atlas = ws.Range("D2")
i = 1
For Each mypath In rng
LookInTheFolder = mypath.Value
Set FileSystemObject = CreateObject("Scripting.FileSystemObject")
For Each searchfolders In FileSystemObject.GetFolder(LookInTheFolder).subfolders
Sheets("Subfolders").Cells(i, 1) = searchfolders
i = i + 1
SearchWithin searchfolders
Next searchfolders
Next mypath
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
Ideally I want to get all the excel files in the folders and subfolders, and copy paste the data on the first sheet into one long list, but I'm still on step 1. I posted a more detailed explanation here last week and have yet to receive any feedback or potential tips.
I apologize if this doesn't make sense or seems half-hazard. I am self taught in excel VBA and am struggling to understand if what I need is even possible. I attempted using Directory but I've little success putting directory in a for each loop.
I also tried using an array, which almost crashed by computer as it went to list ALL the folders and files in my entire computer.
If I understand correctly, your requirements are as follows:
Begin with a set of root paths
Iterate recursively through all the files in each root path
For each file in the resulting collection, if it's an Excel file, add to final list for further processing
Let's start with the first two points. I would suggest the following code (make sure to add a reference to Microsoft Scripting Runtime via Tools -> References... in the VBA editor menus):
Public Function GetFiles(ByVal roots As Variant) As Collection
Select Case TypeName(roots)
Case "String", "Folder"
roots = Array(roots)
End Select
Dim results As New Collection
Dim fso As New Scripting.FileSystemObject
Dim root As Variant
For Each root In roots
AddFilesFromFolder fso.GetFolder(root), results
Next
Set GetFiles = results
End Function
Private Sub AddFilesFromFolder(folder As Scripting.folder, results As Collection)
Dim file As Scripting.file
For Each file In folder.Files
results.Add file
Next
Dim subfolder As Scripting.folder
For Each subfolder In folder.SubFolders
AddFilesFromFolder subfolder, results
Next
End Sub
The GetFiles function can be called by passing in a single string (or Folder):
Debug.Print GetFiles("c:\users\win8\documents").Count
or anything that can be iterated over with For Each -- an array, collection, Dictionary, or even an Excel Range object:
Dim allFiles As Collection
Set allFiles = GetFiles(ws2.Range("A1:A" & Rows.Count).End(xlUp)) 'from question
GetFiles as it stands is flexible for many use cases, and doesn't use any Excel-specific objects. In order to limit the results to Excel files only, you can create a new collection, and only add the Excel files into the new collection:
'You could filter by the File object's Type property
Sub GetExcelFilesByType()
Dim allFiles As Collection
Set allFiles = GetFiles(ws2.Range("A1:A" & Rows.Count).End(xlUp)) 'from question
Dim excelFiles As New Collection
Dim file As Scripting.File
For Each file In allFiles
If file.Type = "Microsoft Excel Worksheet" Then excelFiles.Add file
Next
End Sub
' Or you could filter by extension, using the FileSystemObject.GetExtensionName method
Sub GetExcelFilesByExtensionName()
Dim allFiles As Collection
Set allFiles = GetFiles(ws2.Range("A1:A" & Rows.Count).End(xlUp)) 'from question
Dim excelFiles As New Collection
Dim fso As New Scripting.FileSystemObject
Dim file As Scripting.File
For Each file In allFiles
Select Case fso.GetExtensionName(file.path)
Case "xls", "xlsb", "xlsm"
excelFiles.Add file
End Select
Next
End Sub
Either will get you a Collection of File objects, of only Excel files, from the set of root folders.
Notes
This code is recursively adding all the files (not just Excel files) into one collection (in GetFiles) and then filtering out the non-Excel files into a new collection. This might be less performant than adding only Excel files into the original collection, but that would limit GetFiles to only this scenario.
If you want to paste the results into an Excel worksheet, you could iterate through excelFiles and paste each path into the sheet. Alternatively, you might convert excelFiles into an array, and use the Excel Range object's Value property to set all the values from the array, without using a For Each.
References
Microsoft Scripting Runtime
FileSystemObject object, GetExtensionName method
File object
Folder object
VBA
Collection object
Here's a quick way, slightly adapted from this answer.
Just add in your folder locations to the path() = ... list and it should work for you. It outputs, in the current excel sheet, the paths of all Excel files in folders you provide.
From there, you can do what you please. (Perhaps throw the file paths in to an array, so you have an array of files you want to open. From there you can do the copying of data).
'Force the explicit delcaration of variables
Option Explicit
Sub ListFiles()
'http://www.xl-central.com/list-the-files-in-a-folder-and-subfolders.html
'Set a reference to Microsoft Scripting Runtime by using
'Tools > References in the Visual Basic Editor (Alt+F11)
'Declare the variables
Dim objFSO As Scripting.FileSystemObject
Dim objTopFolder As Scripting.Folder
Dim strTopFolderName As String
Dim path() As Variant ' EDIT THE BELOW PATH LIST FOR WHATEVER YOU NEED!
path() = Array("C:\Users\USERNAME\Desktop\Stuff\New folder", "C:\Users\USERNAME\Desktop\Other Stuff\")
'Insert the headers for Columns
Range("A1").Value = "File Name"
Range("D1").Value = "File Path"
Dim i As Long
For i = LBound(path) To UBound(path)
strTopFolderName = path(i)
'Create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Get the top folder
Set objTopFolder = objFSO.GetFolder(strTopFolderName)
'Call the RecursiveFolder routine
Call RecursiveFolder(objTopFolder, True)
'Change the width of the columns to achieve the best fit
Columns.AutoFit
Next i
End Sub
Sub RecursiveFolder(objFolder As Scripting.Folder, _
IncludeSubFolders As Boolean)
'Declare the variables
Dim objFile As Scripting.File
Dim objSubFolder As Scripting.Folder
Dim NextRow As Long
'Find the next available row
NextRow = Cells(Rows.Count, "A").End(xlUp).Row + 1
'Loop through each file in the folder
For Each objFile In objFolder.Files
Debug.Print (objFile)
If objFile.Type = "Microsoft Excel Worksheet" Then
Cells(NextRow, "A").Value = objFile.Name
Cells(NextRow, "D").Value = objFile.path
NextRow = NextRow + 1
End If
Next objFile
'Loop through files in the subfolders
If IncludeSubFolders Then
For Each objSubFolder In objFolder.SubFolders
Call RecursiveFolder(objSubFolder, True)
Next objSubFolder
End If
End Sub
I want to:
Copy the used range of a sheet called "Kommentar"
Create a ".txt" file ("Kommentar.txt") in the same directory as ThisWorkbook
Paste the previously copied used range
Save the ".txt" file
I have:
Sub CreateAfile()
Dim pth As String
pth = ThisWorkbook.path
Dim fs As Object
Set fs = CreateObject("Scripting.FileSystemObject")
Dim a As Object
Set a = fs.CreateTextFile(pth & "\Kommentar.txt", True)
Dim sh As Worksheet
Set sh = ThisWorkbook.Sheets("Kommentar")
Dim rng As Range
Set rng = sh.UsedRange
a.WriteLine (rng)
a.Close
End Sub
I get
run-time error '13' Mismatch
In line a.WriteLine (rng) the function doesn't accept range to be written.
Since your range is probably made up of several cells, you have to loop through them to get all the text into a string variable. If you use a Variant variable you can copy the values and automatically get an array with the correct dimensions of all the data in the cells, then loop it and copy the text:
Function GetTextFromRangeText(ByVal poRange As Range) As String
Dim vRange As Variant
Dim sRet As String
Dim i As Integer
Dim j As Integer
If Not poRange Is Nothing Then
vRange = poRange
For i = LBound(vRange) To UBound(vRange)
For j = LBound(vRange, 2) To UBound(vRange, 2)
sRet = sRet & vRange(i, j)
Next j
sRet = sRet & vbCrLf
Next i
End If
GetTextFromRangeText = sRet
End Function
Call the function in your code by replacing the a.WriteLine (rng) line with the following:
Dim sRange As String
sRange = GetTextFromRangeText(rng)
Call a.WriteLine(sRange)
Not sure you can do that. I believe you would have to write it out line by line.
Here is an alternative option.
Rather than use the FSO, you could just try saving the sheet as a .txt file.
Here's some sample code.
Credit should goto http://goo.gl/mEHVx
Option Explicit
'Copy the contents of a worksheet, and save it as a new workbook as a .txt file
Sub Kommentar_Tab()
Dim wbSource As Workbook
Dim wsSource As Worksheet
Dim wbDest As Workbook
Dim fName As String
'References
Set wbSource = ActiveWorkbook
Set wsSource = ThisWorkbook.Sheets("Kommentar")
Set wbDest = Workbooks.Add
'Copy range on original sheet
'Using usedrange can be risky and may return the wrong result.
wsSource.UsedRange.Copy
'Save in new workbook
wbDest.Worksheets(1).Cells(1, 1).PasteSpecial Paste:=xlPasteValuesAndNumberFormats
Application.CutCopyMode = False
'Get file name and location
fName = ThisWorkbook.Path & "\Kommentar.txt"
'Save new tab delimited file
wbDest.SaveAs fName, xlText
wbDest.Close SaveChanges:=True
End Sub
Let's say yourRange is the range you want to copy as string.
Use yourRange. Copy to copy it.
After you copied it, Excel saves the text value to the clipboard. Cells in a row separated by tabs, and every row ends with an enter. You can use DataObject's GetFromClipboard and GetText method to save it to a string variable.
Use CreateTextFile to save it to a file.
#xwhitelight gives a good outline. Thanks. But I needed to supply details to myself to accomplish my own task and thought I'd share.
First, a Reference to Microsoft Scripting Runtime and another to Microsoft Forms 2.0 Object Library are required.
The coding details I added to produce an output file follow.
Note that textfilename is the fully-qualified name of the output file that contains the spreadsheet range.
Note that textfilename is opened in the last line of the sub, which isn't necessary, but it's reassuring to SEE what the file contains. Of course, the MsgBox is also unnecessary.
Sub turnRangeIntoTextFile(rg As Range, textfilename As String)
Dim textFile as TextStream
Dim fs As FileSystemObject
Dim myData As DataObject
Set myData = New DataObject
Set fs = CreateObject("Scripting.FileSystemObject")
rg.Copy
myData.GetFromClipboard
MsgBox myData.GetText ' reassurance (see what I got)
Set textFile = fs.CreateTextFile(textfilename, True)
textFile.WriteLine (myData.GetText)
textFile.Close
CreateObject("Shell.Application").Open (textfilename)
End Sub
I am trying to read a specific set of columns from a workbook (every week it is a new workbook) and copy them into another workbook. This I have been able to do, but I think there is a cleaner way to do it!! My code is very bulky and problematic, as every week I need to read info from a different workbook so I have to go back into the code and change the workbook file name. I would love any input on how to improve the code and speed up changing the file name for the workbook from which columns are copied....for example is it possible to ask the user to input the file name in place of a static name??
Any feedback / suggestions are greatly appreciated!!! My code is below:
Sub CopyColumnToWorkbook()
Dim sourceColumns As Range, targetColumns As Range
Dim qw As Range, rw As Range
Dim sd As Range, fd As Range
Dim bu As Range, hu As Range
Dim zx As Range, gx As Range
Dim op As Range, wp As Range
Dim ty As Range, ly As Range
Set sourceColumns = Workbooks("WERT_2013_01_24.xlsx").Worksheets(1).Columns("L")
Set targetColumns = Workbooks("LU.xls").Worksheets(1).Columns("A")
Set qw = Workbooks("WERT_2013_01_24.xlsx").Worksheets(1).Columns("G")
Set rw = Workbooks("LU.xls").Worksheets(1).Columns("B")
Set sd = Workbooks("WERT_2013_01_24.xlsx").Worksheets(1).Columns("C")
Set fd = Workbooks("LU.xls").Worksheets(1).Columns("C")
Set bu = Workbooks("WERT_2013_01_24.xlsx").Worksheets(1).Columns("N")
Set hu = Workbooks("LU.xls").Worksheets(1).Columns("D")
Set zx = Workbooks("WERT_2013_01_24.xlsx").Worksheets(1).Columns("R")
Set gx = Workbooks("LU.xls").Worksheets(1).Columns("E")
Set op = Workbooks("WERT_2013_01_24.xlsx").Worksheets(1).Columns("S")
Set wp = Workbooks("LU.xls").Worksheets(1).Columns("F")
Set ty = Workbooks("WERT_2013_01_24.xlsx").Worksheets(1).Columns("I")
Set ly = Workbooks("LU.xls").Worksheets(1).Columns("G")
sourceColumns.Copy Destination:=targetColumns
qw.Copy Destination:=rw
sd.Copy Destination:=fd
bu.Copy Destination:=hu
zx.Copy Destination:=gx
op.Copy Destination:=wp
ty.Copy Destination:=ly
End Sub
A simple way to neatly take user input is to use the InputBox function
Sub ReadInputBox()
Dim readWorkbookLocation As String
readWorkbookLocation = InputBox("What is the name of the workbook you wish to read from?", "Workbook Select")
MsgBox workbookFile
End Sub
Yup. You can use the Application.GetOpenFilename to let the user choose the file name. For example
Option Explicit
Sub Sample()
Dim Ret
Dim Wb As Workbook
Dim ws As Worksheet
Ret = Application.GetOpenFilename("Excel Files (*.xls*), *.xls*")
If Ret <> False Then
Set Wb = Workbooks.Open(Ret)
Set ws = Wb.Sheets("Sheet1")
With ws
'
'~~> Do whatever you want to do here with the worksheet
'
End With
End If
End Sub
EDIT: I just noticed that You have tagged it as excel-vba-mac as well. If you are doing this on Excel 2011 then see this link which shows how to use the Application.GetOpenFilename. The rest of the code remains as it is.
For these type of operations, I use a separate Excel file that contains the vb codes. (I'll call this file "Operation"). On a worksheet put the name of the source/destination file. Add buttons such as "Select Source", "Select Destination", which would prompt for a file but put the selected filename on the sheet only. Another button "Go" will do the actual operation using the files specified, something like:
If the columns to be copied seldom change, you can leave it inside VBA. If it changes from time to time or you need several versions, put it on the Operation worksheet too. If you need a more complex scenario, you can put the configuration on another worksheet in the sources/destination workbooks, so authors can specify the columns themselves.
As a suggestion for your code, use constants/variable for the filenames, to minimize typing when changing filenames manually. Also assign to variables the Workbook and Worksheet being operated on.
' OPERATIONS SHEET
Dim operWB as Workbook
Dim operWS as Worksheet
Set operWB = Application.ActiveWorkbook
Set operWS = operWB.ActiveSheet
' SOURCE
Dim srcFN as string
' HARDCODED: same as before
'srcFN = "WERT_2013_01_24.xlsx"
' OR get from Cell C2
srcFN = operWS.Cell( 2, 3 )
Dim srcWB as Workbook
Dim srcWS as Worksheet
Set srcWB = Workbooks.Open( srcFN )
Set srcWS = srcWB.Worksheets( 0 )
' DESTINATION
.... do the same ...
.... OPTION 1: COPY ....
Set srcRange = srcWS.Columns( "L" ) ' <-- or get from B10
Set dstRange = dstWS.Columns( "A" ) ' <-- or get from C10
srcRange.Copy Destination:=dstRange
....
.... OPTION 2: COPY AS LOOP ....
Dim currentRow As Integer
currentRow = 10
' keep going while B10, B11... is not empty
While operWS.Cells(currentRow, 2) <> ""
Set srcRange = srcWS.Columns( operWS.Cells(currentRow, 2) ) ' B10, B11 ...
Set dstRange = dstWS.Columns( operWS.Cells(currentRow, 3) ) ' C10, C11 ...
srcRange.Copy Destination:=dstRange
currentRow = currentRow + 1
Wend
You may use the following simple code to loop over all the files in the folder without knowing their names and quantity:
LoopFileNameExt = Dir(InputFolder & "*.xls?")
Do While LoopFileNameExt <> ""
'your code here
LoopFileNameExt = Dir
Loop
Wildcards are allowed in filemask. Good luck!