I have some data files (.dat) that are very large (exceed the 1,048,000 rows Excel allows). I can't quite figure out what the problem is with the attempted macros (originally written for text files with "," delimiter, not .dat files with tab delimiter). The macro works, however it causes the data to be compiled into one column (ex. supposed to be 5 columns, now 1 column with all the numbers as a long text string). Is there a better way to open a very large .dat file, split it up and import it into separate worksheets while keeping the data in separate columns using the tab delimiter?
Sub ImportBigFile()
Dim N As Long
Dim Lim As Long
Dim SS() As String
Dim S As String
Dim R As Long
Dim C As Long
Dim WS As Worksheet
Dim FNum As Integer
Dim FName As String
FName = "C:\Folder 1\Folder 2\File.dat"
FNum = FreeFile
With ActiveWorkbook.Worksheets
Set WS = .Add(after:=.Item(.Count))
End With
Lim = WS.Rows.Count
Open FName For Input Access Read As #FNum
R = 0
Do Until EOF(FNum)
R = R + 1
Line Input #FNum, S
SS = Split(S, "\t", -1)
For C = LBound(SS) To UBound(SS)
WS.Cells(R, C + 1).Value = SS(C)
Next C
If R = Lim Then
With ActiveWorkbook.Worksheets
Set WS = .Add(after:=.Item(.Count))
End With
R = 0
End If
Loop
End Sub
SS = Split(S, "\t", -1)
should be
SS = Split(S, chr$(9), -1)
Assuming your tab is ascii
This fixes 2 issues, and improves performance
As mentioned, the delimiter used in Split (vbTab)
You open the file for Input but never close it
Uses an array to convert to range format, then places it on the range in one operation
Test file used contains 3,145,731 Rows and 5 Cols (122 Mb)
your code: 3.9 min (231.755 sec)
this code: 1.1 Min ( 64.966 sec)
Option Explicit
Public Sub ImportBigFile2()
Const fName = "C:\Folder 1\Folder 2\File.dat"
Dim maxR As Long, maxC As Long, wsCount As Long, arr As Variant, rng As Variant
Dim fNum As Long, fText As String, ws As Worksheet, ln As Variant, nextR As Long
Dim i As Long, r As Long, c As Long, t As Double, ubArr As Long
t = Timer: fNum = FreeFile: maxR = ThisWorkbook.Worksheets(1).Rows.Count
Open fName For Input Access Read As #fNum
fText = Input$(LOF(1), 1)
Close #fNum
arr = Split(fText, vbCrLf): ubArr = UBound(arr)
maxC = UBound(Split(arr(0), vbTab)) + 1
wsCount = ubArr \ maxR + 1: nextR = 0
Application.ScreenUpdating = False
With ThisWorkbook.Worksheets
For i = 1 To wsCount
Set ws = .Add(After:=.Item(.Count))
ReDim rng(1 To maxR, 1 To maxC)
For r = 1 To maxR
ln = Split(arr(nextR), vbTab)
For c = 1 To UBound(ln) + 1
rng(r, c) = ln(c - 1)
Next
nextR = nextR + 1: If nextR > ubArr Then Exit For
Next
ws.Range(ws.Cells(1, 1), ws.Cells(maxR, maxC)) = rng
Next
End With
Application.ScreenUpdating = True
Debug.Print "Time: " & Format(Timer - t, "#,###.000") & " sec" 'Time: 64.966 sec
End Sub
Before (CSV file)
After
Related
Target - to combine multiple 2D arrays from multiple Excel files into single 2D array. I'm first time in coding and VBA.
Sub RangeToArray()
Dim s As String, MyFiles As String
Dim i As Long, j As Long, r As Long, m As Long, n As Long
Dim dArray() As Variant, fArray() As Variant
Dim wb As Workbook, rng As Range
MyFiles = "path"
s = Dir(MyFiles & "*.xls")
Do While s <> ""
ReDim fArray(ubounddArray1, ubounddArray2)
Set wb = Workbooks.Open(MyFiles & s, False, True)
Set rng = wb.Sheets(1).Range("A1:B2")
dArray = rng.Value
uboundfArray1 = UBound(fArray, 1)
uboundfArray2 = UBound(fArray, 2)
ubounddArray1 = UBound(dArray, 1)
ubounddArray2 = UBound(dArray, 2)
ReDim Preserve fArray(uboundfArray1, uboundfArray2 + bounddArray2 + 1)
For m = LBound(dArray, 1) To UBound(dArray, 1)
For n = LBound(dArray, 2) To UBound(dArray, 2)
fArray(m, uboundfArray2 + n) = dArray(m, n)
Next n
Next m
wb.Close SaveChanges:=False
s = Dir
Loop
Don't work. Write Run-time error '9':
Subscript out of range.
Untested, but this may be one way to approach it:
Sub RangeToArray()
Dim s As String, MyFiles As String
Dim fArray() As Variant, arr, i As Long
Dim numRows As Long, numCols As Long, r As Long, c As Long, rT As Long
Dim wb As Workbook, colArrays As Collection
Application.DisplayAlerts = False
Application.ScreenUpdating = False
MyFiles = "C:\Users\User\Desktop\Nezavisimai\Papka2\"
s = Dir(MyFiles & "*.xls")
Set colArrays = New Collection
Do While s <> ""
With Workbooks.Open(MyFiles & s, False, True)
colArrays.Add .Sheets(1).Range("A1:B2").Value 'add array to collection
.Close False
End With
s = Dir
Loop
numRows = UBound(colArrays(1), 1)
numCols = UBound(colArrays(1), 2) 'edit:fixed typo
ReDim fArray(1 To (numRows*colArrays.Count), 1 to numCols)
rT = 0
'loop over collection and add each item to the final array
For Each arr In colArrays
For r = 1 To numRows
rT = rT + 1
For c = 1 To numCols
fArray(rT, c) = arr(r, c)
Next c
Next r
Next arr
Worksheets("Insert").Range("A1") _
.Resize(UBound(fArray, 1), UBound(fArray, 2)).Value = fArray
End Sub
I started VBA - programming 2 days ago and I'm having efficiency problems given my limited knowledge of VBA and EXCEL.
I'm moving data from .CSV to .xlsm files. The .CSV file i receive is structured as
SHEET;COL;ROW;VALUE.
This .CSV is then read to a multidimension array and populates an excel file withwb.Worksheets(ARRAY(i, SHEET)).Cells(R,C) = ARRAY(i, VALUE)
To my understanding, applying the array to a range of cells does not work, as there is no continuous surface to which i'm sending each individual .csv row.
What i've tried most of it can be seen below. I believe a big issue i'm having is the pass-through between VBA -> EXCEL for each .CSV row. Is there any way this can be done by bulk instead?
All types of comments about efficiency and general how-to in VBA is greatly appreciated!
Option Explicit
Private Sub imp_Data()
'----------------------------File Dialog for data input-----------------
Dim Valarr As Variant
Dim fullpath As String
Dim CSVSHEET As Integer, CSVCOL As Integer, CSVROW As Integer, CSVVALUE As Integer
fullpath = [YOUR TEST FILE.CSV]
'----------------------------Read rawdata----------------------------
Dim RawData As String
Open fullpath For Binary As #1
RawData = Space$(LOF(1))
Get #1, , RawData
Close #1
'----------------------------Split rawdata into array-------------------
Dim r As Long, Nrow As Long, Ncol As Long
Dim c As Integer
Dim lineArr As Variant, cellArr As Variant
If Len(RawData) > 0 Then
'Split each row in CSV to str array
lineArr = Split(Trim$(RawData), vbCrLf)
'Dim final array
Nrow = UBound(lineArr) + 1
Ncol = UBound(Split(lineArr(0), ";")) + 1
ReDim Valarr(1 To Nrow, 1 To Ncol)
'Split each col on delimiter ";"
For r = 1 To Nrow
If Len(lineArr(r - 1)) > 0 Then
cellArr = Split(lineArr(r - 1), ";")
For c = 1 To Ncol
Valarr(r, c) = cellArr(c - 1)
Next c
End If
Next r
Else
Debug.Print "No data read"
' do more stuff
End If
'----------------------------Read Table positions-----------------------
Dim i As Integer
For i = 1 To Ncol
If UCase(Valarr(1, i)) = "SHEET" Then
CSVSHEET = i
ElseIf UCase(Valarr(1, i)) = "COL" Then
CSVCOL = i
ElseIf UCase(Valarr(1, i)) = "ROW" Then
CSVROW = i
ElseIf UCase(Valarr(1, i)) = "VALUE" Then
CSVVALUE = i
End If
Next i
'Turn off calculation and screen update for efficiency
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
'--------------------------------------------Send data to Cells----------
Dim L As Long
Dim wb As Workbook
Set wb = ThisWorkbook
L = UBound(Valarr, 1) - LBound(Valarr, 1) + 1
For i = 2 To L
If IsEmpty(Valarr(i, 1)) = 0 Then
wb.Worksheets(Valarr(i, CSVSHEET)).Cells(Valarr(i, CSVROW), Valarr(i, CSVCOL)) = Valarr(i, CSVVALUE)
End If
Next i
'Release ValArr memory
ReDim Valarr(0)
Erase Valarr
'Reapply calculation/screen update
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
From tests with 1 million rows it takes 14 seconds to read data to array and 5+ minutes to move the data to their designated cell. So the issue in the code below is (what i believe)
For i = 2 To L
If IsEmpty(Valarr(i, 1)) = 0 Then
wb.Worksheets(Valarr(i, CSVSHEET)).Cells(Valarr(i, CSVROW), Valarr(i, CSVCOL)) = Valarr(i, CSVVALUE)
End If
Next i
I have an excel file with one column with data. Something like:
21/07/2017
DEF
GHI
Field 7
SOMETHING HERE
MORE TEXT
21/07/2017
DEF
GHI
Field 7
This is repeated a few thousand times. What I am looking for is all rows between and including 21/07/2017 and Field 7 to be deleted and for the rows to be moved up.
I've tried a few things but now back to a blank canvas! Any hints?
Thanks
CODE I TRIED
I get an Overflow error
Sub deleteRows()
Dim sh As Worksheet
Dim rw As Range
Dim RowCount As Integer
RowCount = 1
Application.DisplayAlerts = False
Set sh = ActiveSheet
For Each rw In sh.Rows
If sh.Cells(rw.Row, 1).Value = "21/07/2017" Then
a = RowCount
End If
If sh.Cells(rw.Row, 1).Value = "Field 7" Then
b = RowCount
Rows(a & ":" & b).Delete
End If
RowCount = RowCount + 1
Next rw
End Sub
This will only loop as many times as the pair exists and delete each block as a whole.
The loop ends the first time that both are not found in the remaining values.
Sub myDelete()
Dim str1 As string
Dim str2 As String
Dim rng As Range
Dim ws As Worksheet
Dim i As Long
Dim j As Long
str1 = "21/07/2017"
str2 = "Field 7"
Set ws = Worksheets("Sheet18") 'change to your worksheet
Set rng = ws.Range("A:A")
Do
i = 0: j = 0
On Error Resume Next
i = Application.WorksheetFunction.Match(str1, rng, 0)
j = Application.WorksheetFunction.Match(str2, rng, 0)
On Error GoTo 0
If i > 0 And j > 0 Then
ws.Rows(i & ":" & j).Delete
End If
Loop Until i = 0 Or j = 0
End Sub
If your date is a true date then change str1 to Double:
Dim str1 As Double
and then assign it as such:
str1 = CDbl(DateSerial(2017, 7, 21))
I am using the code below to generate the single file for selected range and considering the first cell in the selected range as file name. Please find the image below for more details[This image shows the selected range,Consider K column(Firstline) and N Column( Lastline) to be in one file and other set of 1st and last line in other file ]this image shows the print file for a single file this is the way m currently using for generating files.I need to create more 30k files so please help me to create more files in single click considering the first and last line as header and footer for the file
Private Sub CommandButton1_Click()
Dim myFile As String, rng As Range, cellValue As Variant, i As Integer, j As Integer, path As String, filename, filename2 As String
path = "D:\Watchlist-Files\"
filename = Selection.Cells(1, 1).Value
filename2 = Left(Mid(filename, 32, 99), Len(Mid(filename, 32, 99)) - 2)
myFile = path & filename2
Set rng = Selection
Open myFile For Output As #1
For i = 1 To rng.Rows.Count
For j = 1 To rng.Columns.Count
cellValue = rng.Cells(i, j).Value
If j = rng.Columns.Count Then
Print #1, cellValue
Else
Print #1, cellValue,
End If
Next j
Next i
Close #1
End Sub
The code below is using a Loop that scans rows in a range that consists of Columns K:N (according to your attached screen-shots).
Assumptions made: your FirstLine is in Column K, and it's the marker of the start position of copying the first cell in the first row.
Your LastLine is in Column N, and it's the marker of the last cell to copy, this is why I am closing the file once it is found.
Edit 1: added a Msgbox to allow the user selection of exporting the entire range or not. In case the user selected NO, then a second InputBox appears that allows the user to enter manually the last row number to export.
Option Explicit
Public Sub CommandButton1_Click()
Dim myFile As String
Dim rng As Range
Dim cellValue As Variant
Dim i As Long
Dim j As Long
Dim LastRow As Long
Dim path As String
Dim filename As String
Dim response As Boolean
path = "D:\Watchlist-Files\"
response = MsgBox("Do you want to Export the entire Range ? ", vbYesNo)
' Export the entire Range
If response = vbYes Then
LastRow = Cells(Rows.Count, "N").End(xlUp).Row
Else ' enter in the inputbox the last row number you want to export
LastRow = InputBox("Enter Last Row Number you wsnt to Export")
End If
Set rng = Range("K2:N" & LastRow)
For i = 2 To LastRow
' Column K holds the file name
If Cells(i, 11) <> "" Then
filename = Left(Mid(Cells(i, 11).Value, 32, 99), Len(Mid(Cells(i, 11).Value, 32, 99)) - 2)
myFile = path & filename
Open myFile For Output As #1
End If
For j = 1 To rng.Columns.Count
cellValue = Cells(i, 10 + j).Value
If j = rng.Columns.Count Then
Print #1, cellValue
' found LastLine >> close the file
If Not cellValue = "" Then
Close #1
End If
Else
Print #1, cellValue,
End If
Next j
Next i
End Sub
Edit 2: Added new code below (to keep the first option valid). The user needs to confirm that every selection he makes start and ends with FirstLine and LastLine , there is no error handling.
Option Explicit Section
Option Explicit
Dim filename As String
Dim path As String
Dim myFile As String
Dim rng As Range
Dim j As Long
Public Sub CommandButton1_Click
Public Sub CommandButton1_Click()
Dim lastRow As Long
Dim Sel_Range As Long
Dim response As Boolean
Dim rowStart() As Long
Dim rowFinish() As Long
path = "D:\Watchlist-Files\"
response = MsgBox("Do you want to Export only the Selected Range ? ", vbYesNo)
If response = True Then
Set rng = Selection
ReDim rowStart(1 To Selection.Areas.Count)
ReDim rowFinish(1 To Selection.Areas.Count)
For Sel_Range = 1 To Selection.Areas.Count
rowStart(Sel_Range) = Selection.Areas(Sel_Range).Row
rowFinish(Sel_Range) = Selection.Areas(Sel_Range).Row + Selection.Areas(Sel_Range).Rows.Count - 1
Call CreateTextFiles(rowStart(Sel_Range), rowFinish(Sel_Range))
Next Sel_Range
Else ' export the entire Range in Columns K:N
lastRow = Cells(Rows.Count, "N").End(xlUp).Row
Set rng = Range("K2:N" & lastRow)
Call CreateTextFiles(2, lastRow)
End If
Sub CreateTextFiles(Sel_StartRow As Long, Sel_FinishRow As Long) - new routine to allow handling of multiple Ranges selection
Sub CreateTextFiles(Sel_StartRow As Long, Sel_FinishRow As Long)
Dim i As Long
Dim cellValue As Variant
For i = Sel_StartRow To Sel_FinishRow
' Column K holds the file name
If Cells(i, 11) <> "" Then
filename = Left(Mid(Cells(i, 11).Value, 32, 99), Len(Mid(Cells(i, 11).Value, 32, 99)) - 2)
myFile = path & filename
Open myFile For Output As #1
End If
For j = 1 To rng.Columns.Count
cellValue = Cells(i, 10 + j).Value
If j = rng.Columns.Count Then
Print #1, cellValue
' found LastLine >> close the file
If Not cellValue = "" Then
Close #1
End If
Else
Print #1, cellValue,
End If
Next j
Next i
End Sub
enter code hereI have my following code a bit short in the process of working on it, so I am trying to get text from a .txt file to be displayed in a cell on Excel, the code will be
Sub citi()
Dim c As Range
Open "C:\Users\alvaradod\Desktop\citi macro\Import File.txt" For Input As #1
R = 0
Dim i As Range
Dim num As Integer
Dim arrData() As String
the_value = Sheets("Prog").Range("A1")
Do Until EOF(1)
Line Input #1, Data
If Not Left(Data, 1) = "" Then
'import this row
R = R + 1
Cells(R, 1).Value = Data
'Mid(the_value, 3, 5)
'Left(Data, Len(Data) - 3)).Value
End If
Loop
For Each i In Range("A1")
i.Select
ActiveCell.Rows("1:1").Mid(Data(i), 49, 5).Select
'ActiveCell.Rows("1:1").EntireRow.Select
Selection.Copy
Sheets("Import").Range("A1").End(xlUp).Offset(num, 0).PasteSpecial
ActiveCell.Rows.Delete
num = num + 1
Next i
End Sub
" LINE 11 WILL PAST THE TEXT FROM LINE ONE ON .TXT FILE TO EXCEL, AFTER THIS FUNCTION I NEED TO TRIM THIS SAME TEXT IN THE EXCEL SHEET TO SHOW THE FIRST 5 CHARACTERS"
Your question isn't very clear, but perhaps something like this?
Sub citi()
Dim oFSO As Object
Dim arrData() As String
Dim arrImport1(1 To 65000) As String
Dim arrImport2(1 To 65000) As String
Dim i As Long, j As Long
Set oFSO = CreateObject("Scripting.FileSystemObject")
arrData = Split(oFSO.OpenTextFile("C:\Test\test.txt").ReadAll, vbCrLf)
For i = LBound(arrData) To UBound(arrData)
If Len(arrData(i)) > 0 Then
j = j + 1
arrImport1(j) = Mid(arrData(i), 3, 5)
arrImport2(j) = Mid(arrData(i), 49, 5)
End If
Next i
If j > 0 Then
Sheets("Sheet1").Range("A1").Resize(j).Value = Application.Transpose(arrImport1)
Sheets("Sheet2").Range("A1").Resize(j).Value = Application.Transpose(arrImport2)
End If
Set oFSO = Nothing
Erase arrData
Erase arrImport
End Sub