saving excel file as tab-delimited text file without quotes - excel

I have an Excel 2010 workbook. I need to save the used range of each of its worksheets as a tab-delimited text file with no quotes, with the same filename as the workbook and with an extension given by the worksheet name.
Note that Excel stupidly surrounds a value by quotes whenever it sees a comma, even though the delimiter is a tab; other than that, the normal "Save As" / "Text (Tab delimited)" would be fine.
I would prefer to do that using VBA code from within Excel.
If there is a Python solution, I'd be interested too. But at this point pywin32 support for Python 3 is only experimental, so I am not sure I can use it.

Ok here is a slightly complex routine which I wrote couple of months back for one of my clients. This code exports the Excel Worksheet to a Fixed Width File without QUOTES. Screenshots also attached. I am sure this code can be made even better :)
TRIED AND TESTED
Option Explicit
'~~> Change this to relevant output filename and path
Const strOutputFile As String = "C:\Output.Csv"
Sub Sample()
Dim ws As Worksheet
Dim rng As Range
Dim MyArray() As Long, MaxLength As Long
Dim ff As Long, i As Long, lastRow As Long, LastCol As Long
Dim strOutput As String
On Error GoTo Whoa
Application.ScreenUpdating = False
'~~> Change this to the respective sheet
Set ws = Sheets("Sheet1")
LastCol = ws.Cells(1, Columns.Count).End(xlToLeft).Column
'~~> Loop through each Column to get the max size of the field
For i = 1 To LastCol
MaxLength = getMaxLength(ws, i)
ReDim Preserve MyArray(i)
MyArray(i) = MaxLength
Next i
ff = FreeFile
'~~> output file
Open strOutputFile For Output As #ff
'~~> Write to text file
With ws
lastRow = .Range("A" & Rows.Count).End(xlUp).Row
For Each rng In .Range("A1:A" & lastRow)
With rng
For i = 1 To UBound(MyArray)
'~~> Insert a DELIMITER here if your text has spaces
strOutput = strOutput & " " & Left(.Offset(0, i-1).Text & _
String(MyArray(i), " "), MyArray(i))
Next i
Print #ff, Mid(Trim(strOutput), 1)
strOutput = Empty
End With
Next rng
End With
LetsContinue:
On Error Resume Next
Close #ff
On Error GoTo 0
Application.ScreenUpdating = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
'~~> Function to get the max size
Public Function getMaxLength(ws As Worksheet, Col As Long) As Long
Dim lastRow As Long, j As Long
getMaxLength = 0
lastRow = ws.Range("A" & ws.Rows.Count).End(-4162).Row
For j = 1 To lastRow
If Len(Trim(ws.Cells(j, Col).Value)) > getMaxLength Then _
getMaxLength = Len(Trim(ws.Cells(j, Col).Value))
Next j
End Function

Open your excel/csv/text
Perform your your required action and then You can saveas using file format as xlTextPrinter
ActiveWorkbook.SaveAs Filename:="Your File Name.txt", FileFormat:=xlTextPrinter, CreateBackup:=False
No need for extra code to replace extra quotes

Related

VBA - Store first value of each row from CSV into a variable and move to specific sheet

I'm relatively new to VBA, I've been wanting to do the following, if at all possible, in VBA
Point towards CSV file
Loop through each row
Using the first value of each row - store the rest of the row in a specific worksheet
I've managed to do points 1 and 2, however, I'm struggling on Point 3
For example:
My CSV file is something like this:
A00,5675,TGI,6897
R88,7647,35968,35864
R88,5968,34531,44566
Z11,2245,FGH,YIU
I would like to read the first value of each row, in this case, A00, R88, R88 and Z11.
Then write that specific line to a corresponding worksheet based on the value.
For example, the A00 line would be written to a worksheet called A00 and so on.
I've gotten this far for reference:
Sub ImportFile()
Dim ws As Worksheet, strFile As String
strFile = Application.GetOpenFilename("Text Files (*.csv),*.csv", , "Please select text file...") 'Select file
'If first value = A00 - write to ActiveWorkbook.Sheets("A00")
'If first value = Z99 - write to ActiveWorkbook.Sheets("Z99")
Set ws = ActiveWorkbook.Sheets("A00") 'Export to this worksheet
With ws.QueryTables.Add(Connection:="TEXT;" & strFile, Destination:=ws.Range("A1"))
.TextFileParseType = xlDelimited
.TextFileCommaDelimiter = True
.Refresh
End With
End Sub
Any help would be much appreciated.
I've put together some VBA code that seems to work how you want. It reads the data file in line by line, splits this data into an array using the "," as a separator, sets the worksheet to the first element of the array, and then places the rest of the elements into cells on the first blank line.
Sub sGetData()
On Error GoTo E_Handle
Dim ws As Worksheet
Dim lngLastRow As Long
Dim strFile As String
Dim intFile As Integer
Dim strInput As String
Dim aData() As String
Dim lngLoop1 As Long
intFile = FreeFile
strFile = "J:\downloads\test3.txt"
Open strFile For Input As intFile
Do
Line Input #intFile, strInput
aData() = Split(strInput, ",")
Set ws = Worksheets(aData(LBound(aData)))
lngLastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
For lngLoop1 = LBound(aData) + 1 To UBound(aData)
ws.Cells(lngLastRow + 1, lngLoop1) = aData(lngLoop1)
Next lngLoop1
Loop Until EOF(intFile)
sExit:
On Error Resume Next
Close #intFile
Exit Sub
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "sGetData", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume sExit
End Sub
It could probably do with a bit of "bullet-proofing", to deal with things like the worksheet not existing and so on, but it should give you an idea.
Regards,

Read Excel file without opening it and copy contents on column first blank cell

So I want to automate a lot of manual work of copy/paste with the help of a Macro. The macro should read all files from folder one by one, copy the content from that source file range "I9:J172" and paste it on the destination file (where the macro is of course) on the column first blank row.
Application.ScreenUpdating = False
'For Each Item In franquicia
' OPEN THE SOURCE EXCEL WORKBOOK IN "READ ONLY MODE".
Set src = Workbooks.Open("C:\folder\inventory.xlsb", True, True)
' GET THE TOTAL ROWS FROM THE SOURCE WORKBOOK.
Dim iTotalRows As Integer
iTotalRows = src.Worksheets("INV").Range("I9:J" & Cells(Rows.Count, "J").End(xlUp).Row).Rows.Count
' FIND FIRST BLANK CELL
Dim LastRow As Long
LastRow = Worksheets("Hoja1").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
' COPY DATA FROM SOURCE (CLOSE WORKGROUP) TO THE DESTINATION WORKBOOK.
Dim iCnt As Integer ' COUNTER.
For iCnt = 1 To iTotalRows
Worksheets("Hoja1").Range("A" & LastRow & ":B" & iCnt).Value = src.Worksheets("INV").Range("I9:J172" & iCnt).Value
Next iCnt
' CLOSE THE SOURCE FILE.
src.Close False ' FALSE - DON'T SAVE THE SOURCE FILE.
Set src = Nothing
'Next Item
I want to solve first this last row problem and then do an array and the loop to read all the files one by one.
Thank you!
The following code does what you've described, and the animated gif demonstrates with 3 test files (with test data in the columns you mentioned). The first part of the gif shows the contents of 2 of the test files, and then runs the macro, stepping through it, showing the result on a "combined" sheet. Click on the gif to see better detail. Note that each test file's data must be on a "data" sheet. You can modify, of course.
Option Explicit
Dim theDir As String, alreadyThere As Boolean, wk As Workbook
Dim sh As Worksheet, comboSh As Worksheet, comboR As Range
Dim r As Range, s As String, numFiles As Integer
Const ext = ".xlsx"
Sub CombineFiles()
Set comboSh = getSheet(ThisWorkbook, "Combined", True)
theDir = ThisWorkbook.Path
s = Dir(theDir & "\*" & ext)
Set comboR = comboSh.Range("A1")
While s <> ""
ThisWorkbook.Activate
If comboR <> "" Then Set comboR = comboR.Offset(0, 2)
comboR.Activate
Set wk = Workbooks.Open(theDir & "\" & s)
Set sh = getSheet(wk, "data", False)
Set r = sh.Range("I9:J72")
'Set r = sh.Range(r, r.End(xlToRight))
'Set r = sh.Range(r, r.End(xlDown))
r.Copy
comboSh.Paste
Application.DisplayAlerts = False
wk.Close False
Application.DisplayAlerts = True
s = Dir()
numFiles = numFiles + 1
Wend
MsgBox ("done")
End Sub
Function getSheet(wk As Workbook, shName As String, makeIfAbsent As Boolean) As Worksheet
alreadyThere = False
For Each sh In wk.Worksheets
If sh.Name = shName Then
alreadyThere = True
Set getSheet = sh
End If
Next
If Not alreadyThere Then
If makeIfAbsent Then
Set getSheet = wk.Sheets.Add
getSheet.Name = shName
Else
MsgBox shName & " sheet not found -- ending"
End
End If
End If
End Function
I may be arriving to the party too late. It seems like you got the solution you were after. For future reference, try the AddIn below. This will do all kinds of copy/paste/merge tasks.
https://www.rondebruin.nl/win/addins/rdbmerge.htm

Excel VBA generating a set of 30 invoices with different

I have a simple question. I have an excel file with an invoice format being linked to around 30 odd invoice details from a table in a different sheet. I have already devised a macro to save each iteration as a separate excel file. But i want the 'filename' to be automated. For eg, if i select to print invoices for records 1-15, filenames should be saved as 1,2,3 and so on. The existing code is as follows -
Sub PrintForms()
Dim StartRow As Integer
Dim EndRow As Integer
Dim Msg As String
Dim i As Integer
Sheets("Invoice").Activate
StartRow = Range("StartRow")
EndRow = Range("EndRow")
If StartRow > EndRow Then
Msg = "ERROR" & vbCrLf & "The starting row must be less than the ending row!"
MsgBox Msg, vbCritical, APPNAME
End If
For i = StartRow To EndRow
Range("rowindex") = i
If Range("Preview") Then
Dim wb As Workbook
Set wb = Workbooks.Add
ThisWorkbook.Sheets("Invoice").Copy Before:=wb.Sheets(1)
wb.SaveAs "C:\workspace\1. Sem-1 Accounts\macrotest1.xlsx"
Else
ActiveSheet.PrintOut
End If
Next i
End Sub
Sub EditData()
Worksheets("Invoice Format").Activate
Range("A1").Select
End Sub
Sub ReturnToForm()
Worksheets("Invoice").Activate
Range("rowindex").Select
End Sub
End Sub

Exporting Multiple range to a txt file

I need to export multiple ranges from different worksheet in to a single text file I want the the cell ranges to append one after another. Currently I'm using this code which works perfectly for one range one worksheet what do I need to modify this to make it work with more ranges?
Example ranges I would like to add
Sheet1 A2:E50
Sheet2 A2:F60
Sheet4 A2:C45
Current code
Sub Export()
Dim r As Range, c As Range
Dim sTemp As String
Open Workbooks("Test.xlsm").Path & "\Test.SQL" For Output As #1
For Each r In Worksheets("SQL1").Range("A1:D50").Rows
sTemp = ""
For Each c In r.Cells
sTemp = sTemp & c.Text & Chr(9)
Next c
'Get rid of trailing tabs
While Right(sTemp, 1) = Chr(9)
sTemp = Left(sTemp, Len(sTemp) - 1)
Wend
Print #1, sTemp
Next r
Close #1
End Sub
Like I mentioned in the above comment this is the fastest way you can export those ranges to text file. No looping required...
Untested
Dim Thiswb As Workbook, thatWb As Workbook
Sub Sample()
Set Thiswb = ThisWorkbook
Set thatWb = Workbooks.Add
CopyRange Thiswb.Sheets("Sheet1"), Thiswb.Sheets("Sheet1").Range("A1:E10000")
CopyRange Thiswb.Sheets("Sheet2"), Thiswb.Sheets("Sheet2").Range("A1:F10000")
CopyRange Thiswb.Sheets("Sheet3"), Thiswb.Sheets("Sheet3").Range("A1:C10000")
Application.DisplayAlerts = False
thatWb.SaveAs "C:\Temp.csv", xlCSV
Application.DisplayAlerts = True
End Sub
Sub CopyRange(ws As Worksheet, rng As Range)
Dim lRow As Long
lRow = thatWb.Sheets(1).Range("A" & thatWb.Sheets(1).Rows.Count).End(xlUp).Row + 1
rng.Copy thatWb.Sheets(1).Range("A" & lRow)
End Sub
Followup from comments
Siddharth this is usful but wont work for me, as my above code plugs in to SQL and JAVA, can you show me how to modify my above code to perform multipul ranges on different sheet regardless of this been the best methord, unfortunatly im not very good with VBA :( – Windmill 5 mins ago
Is this what you are trying? (Untested)
Sub Sample()
Dim Thiswb As Workbook
Set Thiswb = ThisWorkbook
Export Thiswb.Sheets("Sheet1").Range("A2:E50")
Export Thiswb.Sheets("Sheet2").Range("A2:F60")
Export Thiswb.Sheets("Sheet4").Range("A2:C45")
End Sub
Sub Export(rng As Range)
Dim r As Range, c As Range
Dim sTemp As String
'~~> Use Append instead of Output
Open Workbooks("Test.xlsm").Path & "\Test.SQL" For Append As #1
For Each r In rng.Rows
sTemp = ""
For Each c In r.Cells
sTemp = sTemp & c.Text & Chr(9)
Next c
'Get rid of trailing tabs
While Right(sTemp, 1) = Chr(9)
sTemp = Left(sTemp, Len(sTemp) - 1)
Wend
Print #1, sTemp
Next r
Close #1
End Sub

Is there a way to write ranges in bulk to a text/CSV file?

I'm used to write the content (values) of a range of cells to a text file with the write command in VBA, for example:
write #myfile, Range("A1").value, Range("A2).value, Range("A3).value
Does there exist a more elegant and convenient built-in method to dump a whole range directly to a delimited file, possibly even over multiple rows at a time? Or has anybody come up with a customized solution? I think that would be incredibly useful.
I wrote you this it could still be improved, but I think it's good enough:
Sub SaveRangeAsCSV(r As Range, filename As String, overwrite As Boolean)
Dim wB As Workbook
Dim c As Range
Dim usedRows As Long
If overwrite Then
If Dir(filename) <> "" Then Kill filename
If Err.Number <> 0 Then
MsgBox "Could not delete previously existing file." & vbNewLine & Err.Number & ": " & Err.Description
Exit Sub
End If
End If
If Dir(filename) <> "" Then
Set wB = Workbooks.Open(filename)
Else
Set wB = Workbooks.Add
End If
With wB.Sheets(1)
usedRows = .UsedRange.Rows.Count
'Check if more than 1 row is in the used range.
If usedRows = 1 Then
'Since there's only 1 row, see if there's more than 1 cell.
If .UsedRange.Cells.Count = 1 Then
'Since there's only 1 cell, check the contents
If .Cells(1, 1) = "" Then
'you're dealing with a blank workbook
usedRows = 0
End If
End If
End If
'Check if range is contigious
If InStr(r.Address, ",") Then
For Each c In r.Cells
.Range(c.Address).Offset(usedRows, 0).Value = c.Value
Next
Else
.Range(r.Address).Offset(usedRows, 0).Value = r.Value
End If
End With
wB.SaveAs filename, xlCSV, , , , False
wB.Saved = True
wB.Close
End Sub
Sub Example()
'I used Selection here just to make it easier to test.
'Substitute your actual range, and actual desired filepath
'If you pass false for overwrite, it assumes you want to append
'It will give you a pop-up asking if you want to overwrite, which I could avoid
'by copying the worksheet and then closing and deleting the file etc... but I
'already spent enough time on this one.
SaveRangeAsCSV Selection, "C:\proofOfConcept.csv", False
End Sub
When using it, just supply the actual range, the actual filename, and whether or not you want to overwrite the file. :) This has been updated to allow non-contiguous ranges. For merged cells it will end up putting the value in the first cell of the merged range.
This is the solution I came up with by myself and suits my needs best as far as I can see:
Sub DumpRangeToTextFile(filehandle As Integer, source As Range)
Dim row_range As Range, mycell As Range
For Each row_range In source.rows
For Each mycell In row_range.cells
Write #filehandle, mycell.Value;
Next mycell
Write #filehandle,
Next row_range
End Sub
Short and sweet! ;)
Still I'm giving Daniel Cook's solution which is also very useful the credit it deserves.
These methods above iterate across the cell ranges in order to export the data. Anything that tends to do with looping over a range of cells in the sheet is extremely slow due to all the error checking.
Here's a way I did it without the iteration. Basically, it makes use of the built in function "Join()" to do the heavy lifting which would be your iteration loop. This is much faster.
The related Read() subroutine I detailed in another posting: https://stackoverflow.com/a/35688988/2800701
This is the Write() subroutine (note: this assumes your text is pre-formatted to the correct specification in the worksheet before you export it; it will only work on a single column...not on multiple column ranges):
Public Sub WriteRangeAsPlainText(ExportRange As Range, Optional textfilename As Variant)
If IsMissing(textfilename) Then textfilename = Application.GetSaveAsFilename(FileFilter:="Text Files (*.txt), *.txt")
If textfilename = "" Then Exit Sub
Dim filenumber As Integer
filenumber = FreeFile
Open textfilename For Output As filenumber
Dim textlines() As Variant, outputvar As Variant
textlines = Application.Transpose(ExportRange.Value)
outputvar = Join(textlines, vbCrLf)
Print #filenumber, outputvar
Close filenumber
End Sub
From my article Creating and Writing to a CSV FIle using Excel VBA
This Article provides two VBA code samples to create and write to a CSV file:
Creating a CSV file using the Open For Output as FreeFile.
Creating a CSV file using the FileSystemObject object.
I prefer the latter approach mainly as I am using the FileSystemObject for further coding, for example processing all files in subfolders recursively (though that technique is not used in this article).
Code Notes
This code must be run from a regular VBA Code Module. Otherwise the
code will cause an error if users try to run it from the ThisWorkbook
or Sheet Code panes given the usage of Const.
It is worth noting that the ThisWorkbook and Sheet code sections
should be reserved for Event coding only, "normal" VBA should be run
from standard Code Modules.
Please note that for purposes of the sample code, the file path of the
CSV output file is "hard-coded" as:
C:\test\myfile.csv at the top of the code. You will probably want to set the output file programmatically, for instance as a
function parameter.
As mentioned earlier; For example purposes, this code TRANSPOSES
COLUMNS AND ROWS; that is, the output file contains one CSV row for
each column in the selected range. Normally, CSV output would be
row-by-row, echoing the layout visible on screen, but I wanted to
demonstrate that generating the output by using VBA code provides
options beyond what is available by, for instance, using the Save
As... CSV Text menu option.
Code
Const sFilePath = "C:\test\myfile.csv"
Const strDelim = ","
'Option 1
Sub CreateCSV_Output()
Dim ws As Worksheet
Dim rng1 As Range
Dim X
Dim lRow As Long
Dim lCol As Long
Dim strTmp As String
Dim lFnum As Long
lFnum = FreeFile
Open sFilePath For Output As lFnum
For Each ws In ActiveWorkbook.Worksheets
'test that sheet has been used
Set rng1 = ws.UsedRange
If Not rng1 Is Nothing Then
'only multi-cell ranges can be written to a 2D array
If rng1.Cells.Count > 1 Then
X = ws.UsedRange.Value2
'The code TRANSPOSES COLUMNS AND ROWS by writing strings column by column
For lCol = 1 To UBound(X, 2)
'write initial value outside the loop
strTmp = IIf(InStr(X(1, lCol), strDelim) > 0, """" & X(1, lCol) & """", X(1, lCol))
For lRow = 2 To UBound(X, 1)
'concatenate long string & (short string with short string)
strTmp = strTmp & (strDelim & IIf(InStr(X(lRow, lCol), strDelim) > 0, """" & X(lRow, lCol) & """", X(lRow, lCol)))
Next lRow
'write each line to CSV
Print #lFnum, strTmp
Next lCol
Else
Print #lFnum, IIf(InStr(ws.UsedRange.Value, strDelim) > 0, """" & ws.UsedRange.Value & """", ws.UsedRange.Value)
End If
End If
Next ws
Close lFnum
MsgBox "Done!", vbOKOnly
End Sub
'Option 2
Sub CreateCSV_FSO()
Dim objFSO
Dim objTF
Dim ws As Worksheet
Dim lRow As Long
Dim lCol As Long
Dim strTmp As String
Dim lFnum As Long
Set objFSO = CreateObject("scripting.filesystemobject")
Set objTF = objFSO.createtextfile(sFilePath, True, False)
For Each ws In ActiveWorkbook.Worksheets
'test that sheet has been used
Set rng1 = ws.UsedRange
If Not rng1 Is Nothing Then
'only multi-cell ranges can be written to a 2D array
If rng1.Cells.Count > 1 Then
X = ws.UsedRange.Value2
'The code TRANSPOSES COLUMNS AND ROWS by writing strings column by column
For lCol = 1 To UBound(X, 2)
'write initial value outside the loop
strTmp = IIf(InStr(X(1, lCol), strDelim) > 0, """" & X(1, lCol) & """", X(1, lCol))
For lRow = 2 To UBound(X, 1)
'concatenate long string & (short string with short string)
strTmp = strTmp & (strDelim & IIf(InStr(X(lRow, lCol), strDelim) > 0, """" & X(lRow, lCol) & """", X(lRow, lCol)))
Next lRow
'write each line to CSV
objTF.writeline strTmp
Next lCol
Else
objTF.writeline IIf(InStr(ws.UsedRange.Value, strDelim) > 0, """" & ws.UsedRange.Value & """", ws.UsedRange.Value)
End If
End If
Next ws
objTF.Close
Set objFSO = Nothing
MsgBox "Done!", vbOKOnly
End Sub

Resources