I am trying to convert a text file into an excel sheet. This is what the format looks like.
I have tried writing a script but currently all it does is overwrites my current text file adding my column headers. It does not add any of the data from my text file. Could anyone help me understand what I am doing wrong.
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True
strInput=InputBox("Enter name of File in C:\Users\spencerr\Desktop\MyProject\bin\")
'ask user for file name
Set wb = objExcel.Workbooks.Open("C:\Users\bob\Desktop\MyProject\bin\" & strInput)
'Delete labels in log
For i = 1 To 5
Set objRange = objExcel.Cells(1, i).EntireColumn
objRange.Delete
Next
Set activeCell = objExcel.Cells(1, 2)
Dim intVal
Dim comVal
Dim primeRow
Dim largestRow
Dim largestDec
Dim row
primeRow = 0
'filter out one measurement per second
Do Until IsEmpty(activeCell)
primeRow = primeRow + 1
'get base integer of first value by chopping off decimal
intVal = Fix(activeCell.Value)
comVal = intVal
'get all consecutive rows that have same base integer
Do While intVal = comVal
row = activeCell.Row
Set activeCell = objExcel.Cells((row + 1), 2)
comVal = Fix(activeCell.Value)
Loop
'highest row number that contains the base integer
largestRow = row
'delete all the rows up to the largest row
j = primeRow
Do While j < largestRow
Set deleteRow = objExcel.Cells(primeRow, 2).EntireRow
deleteRow.Delete
j = j + 1
Loop
'compare the value right below the exact second and the value right above to see
'which is closer to the exact second
Set activeCell = objExcel.Cells(primeRow, 2)
largestDec = activeCell.Value
Set activeCell = objExcel.Cells((primeRow + 1), 2)
comVal = activeCell.Value
if (((intVal + 1) - largestDec) > (comVal - (intVal + 1))) Then
objExcel.Cells(primeRow, 2).EntireRow.Delete
End If
Loop
'round all the seconds that are left to the nearesr second
Set activeCell = objExcel.Cells(1, 2)
Do Until IsEmpty(ActiveCell)
row = activeCell.row
objExcel.Cells(row, 2) = Round(activeCell.Value)
Set activeCell = objExcel.Cells(row + 1, 2)
Loop
'add labels for KML conversion
objExcel.Cells(1,1).EntireRow.Insert
objExcel.Cells(1, 2).Value = "Description"
objExcel.Cells(1, 3).Value = "Latitude"
objExcel.Cells(1, 4). Value = "Longitude"
wb.Save
wb.Close
objExcel.Quit
I'd use a regular expression to transform the data into CSV format:
Set fso = CreateObject("Scripting.FileSystemObject")
Set inFile = fso.OpenTextFile("C:\path\to\input.txt")
Set outFile = fso.OpenTextFile("C:\path\to\output.csv", 2, True)
Set re = New RegExp
re.Pattern = "^week: (\d+) seconds: (\d+\.\d+) x: (\d+\.\d+) " & _
"y: (-\d+\.\d+) heading: (\d+)$"
re.IgnoreCase = True
outFile.WriteLine "Week,Seconds,X,Y,Heading"
Do Until inFile.AtEndOfStream
For Each m In re.Execute(inFile.ReadLine)
outFile.WriteLine m.Submatches(0) & "," & m.Submatches(1) & "," & _
m.Submatches(2) & "," & m.Submatches(3) & "," & m.Submatches(4)
Next
Loop
inFile.Close
outFile.Close
Then you can open the CSV file with Excel and save it as a workbook.
I'll throw out another solution. Just use Excel's TextToColumns() function. Tell it to use Space (8th argument = True) as a delimiter and to Treat consecutive delimiters as one (4th argument = True).
Const xlDelimited = 1
Const xlDoubleQuote = 1
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True
objExcel.Workbooks.Open "c:\path\to\text.txt"
With objExcel.ActiveSheet
.Columns("A:A").TextToColumns .Range("A1"), xlDelimited, xlDoubleQuote, True, , , , True
End With
Or, in long form:
With objExcel.ActiveSheet
.Columns("A:A").TextToColumns _
.Range("A1"), _ ' Destination
xlDelimited, _ ' Data Type
xlDoubleQuote, _ ' Text Qualifier
True, _ ' Consecutive Delimiters?
, _ ' Use Tab for Delimiter?
, _ ' Use Semicolon for Delimiter?
, _ ' Use Comma for Delimiter?
True ' Use Space for Delimiter?
End With
That will get your data into proper columns. Then just delete the "label" columns:
.Range("A:A,C:C,E:E,G:G,I:I").Delete
and save it as a CSV.
From the comments, I think it might be easier to skip VBA, change the .txt file extension into .csv. Then, when you open this in Excel, you'll get a column with all of your data.
Along the top, under the "Data" tab, you'll see "Text to Columns". Highlight column A, choose "Text to Columns", then choose "Delimited", and hit "Next", then choose "Space". You'll see a preview of how the data will be split up below it. If that looks good, you can click "Finish" to overwrite Col. A with the new split up data, or click "Next" to choose a specific cell to start in.
That should get you pretty far, and it may not be perfect, so let me know what it looks like after this (or if you have any questions).
A faster way: convert the file to csv.
Since your source is fixed width the easiest way is to just copy the bits you need to a new line.
sep = ";" 'or , (depends on your language settings)'
header = "week" & sep & "seconds" & sep
line = "WEEK: 1799 SECONDS: 251731.358 X:32.896391 Y:-117.200281 Heading: 178"
csvLine = mid(line,7,4) & sep & mid(line,22,10) & sep 'etc..'
'write to your csv file, here I only echo to the screen
Wscript.echo header
Wscript.echo csvLine
'week;seconds;
'1799;251731.358;
Using this method is faster end you don't need Excel installed on the pc
Related
I have 2 CSV files as shown below.
TEAMLIST.csv:
empid,name
54321,xyz
12345,abc
DATA.csv:
heading1,head2,head3,head4
54321-Process : GDPR_WBT,54321,Process : GDPR_WBT,TRUE
12345-Process : GDPR_WBT,12345,Process : GDPR_WBT,TRUE
54321-Fire Safety,54321,Fire Safety,FALSE
12345-Fire Safety,12345,Fire Safety,TRUE
Below is my entire VBScript code. The problem is that the inner loop is working fine but the outer loop is taking only the first record [54321,xyz] and not all records. Not able to understand why.
Option Explicit
Dim fs
Dim fs2
Set fs = CreateObject("Scripting.FileSystemObject")
Set fs2 = CreateObject("Scripting.FileSystemObject")
Dim EMPLOYEE
Dim DATA
Set EMPLOYEE = fs.OpenTextFile("TEAMLIST.csv")
Set DATA = fs2.OpenTextFile("DATA.csv")
Dim counter, line, EMP_ARRAY
Dim counter2, line2, DATA_ARRAY
counter = 0
counter2 = 0
Do While Not EMPLOYEE.AtEndOfStream
line = EMPLOYEE.ReadLine
counter = counter + 1
If counter > 1 Then
EMP_ARRAY = Split(line, ",")
Do While Not DATA.AtEndOfStream '### DATA LOOP STARTS ###
line2 = DATA.ReadLine
counter2 = counter2 + 1
If counter2 > 1 Then
DATA_ARRAY = Split(line2, ",")
If EMP_ARRAY(0) = DATA_ARRAY(1) Then
If DATA_ARRAY(2) = "Process : GDPR_WBT" Then
If DATA_ARRAY(3) = "" Then
DATA_ARRAY(3) = "FALSE"
End If
WScript.Echo EMP_ARRAY(0) & "--" & EMP_ARRAY(1) & "--" & DATA_ARRAY(2) & "--" & DATA_ARRAY(3)
End If
If DATA_ARRAY(2) = "Fire Safety" Then
If DATA_ARRAY(3) = "" Then
DATA_ARRAY(3) = "FALSE"
End If
WScript.Echo EMP_ARRAY(0) & "--" & EMP_ARRAY(1) & "--" & DATA_ARRAY(2) & "--" & DATA_ARRAY(3)
End If
End If
End If
Loop '### DATA LOOP ENDS ###
End If
Loop
EMPLOYEE.Close
DATA.Close
Set EMPLOYEE = Nothing
Set DATA = Nothing
Set fs = Nothing
Set fs2 = Nothing
After the first iteration of the outer loop the inner loop has already read DATA.csv to the end. To "rewind" that file for each iteration of the outer loop you need to open/close it inside the outer loop.
Do Until EMPLOYEE.AtEndOfStream
'...
Set DATA = fs.OpenTextFile("DATA.csv")
Do Until DATA.AtEndOfStream
'...
Loop
DATA.Close
Loop
Alternatively (if the file is sufficiently small) read it into an array once and have the inner loop iterate over that array.
DATA = Split(fs.OpenTextFile("DATA.csv").ReadAll, vbNewLine)
Do Until EMPLOYEE.AtEndOfStream
'...
For Each line2 In DATA
'...
Next
Loop
Side-note 1: creating multiple FileSystemObject instances in your script is pointless. Create a single instance at the beginning of your script and use that instance throughout the rest of the code.
Side-note 2: Do While Not is awkward. Use Do Until instead.
I have a ms access table that is tracking 50 products with their daily sold volumes. I would like to export using vba 1 csv file (including headers) for each product showing the daily volumes from a recordset without saving the recordset to a permanent query. I am using the below code but I am stuck at the point of the actual export highlighted below in code.
Any assistance in fixing this is appreciated.
Dim rst As Recordset
Dim rstId As Recordset
SQLExportIds = "SELECT DISTINCT tblDailyVols.SecId FROM tblDailyVols WHERE tblDailyVols.IsDeleted=False"
Set rstId = CurrentDb.OpenRecordset(SQLExportIds)
If rstId.EOF = True Then
MsgBox "No Products Found"
Exit Sub
End If
Do While rstId.EOF = False
SecId = rstId.Fields("SecId")
SQLExportQuotes = " SELECT tblDailyVols.ID , tblDailyVols.TradedVolume, tblDailyVols.EffectiveDate FROM tblDailyVols "
SQLExportQuotes = SQLExportQuotes & " WHERE tblDailyVols.IsDeleted=False and tblDailyVols.ID = " & SecId
SQLExportQuotes = SQLExportQuotes & " ORDER BY tblDailyVols.EffectiveDate "
Set rst = CurrentDb.OpenRecordset(SQLExportQuotes)
If rst.EOF = True Then
MsgBox "No Quotes Found"
Exit Sub
End If
IDFound = rst.Fields("ID")
OutputPlace = “C:\Output” & IDFound & ".csv"
Set qdfTemp = CurrentDb.CreateQueryDef("", SQLExportQuotes)
**DoCmd.TransferText acExportDelim, , 1, OutputPlace, True** <--This Here Line Fails
Set rst = Nothing
rstId.MoveNext
Loop
Set rstId = Nothing
You will have to create an actual named QueryDef object for TransferText to work with, but then you can just delete it afterwards. Something like this:
Set qdfTemp = CurrentDb.CreateQueryDef("zzzTemp", SQLExportQuotes)
Set qdfTemp = Nothing
DoCmd.TransferText acExportDelim, , "zzzTemp", OutputPlace, True
DoCmd.DeleteObject acQuery, "zzzTemp"
You asked for a VBA solution, and I detect a preference for not creating new Access objects; you may well have good reasons for that, but the 'pure' VBA solution is a lot of work.
A solution that implements encapsulating text fields in quotes is the bare minimum for a competent answer. After that, you need to address the three big issues:
Optimising away VBA's clunky string-handling;
The Byte Order Marker, which VBA embeds in every string it saves to
file, ensuring that some of the most common consumers of a csv file
cannot read it properly;
...And there's rarely any middle ground between writing the file
line-by-line, forever, and writing it in one big chunk that'll throw
an out-of-memory error on larger recordsets.
Beginners in VBA may find the string-optimisations difficult to understand: the biggest performance gain available in native VBA is to avoid string allocation and concatenation ( here's why: http://www.aivosto.com/vbtips/stringopt2.html#huge ) - so I use join, split, and replace instead of myString = MyString & MoreString
The trailing loop, with the RecordSet.GetRows() call at the very end, will raise eyebrows among coders with strong opinions about structured programming: but there are constraints on how you can order the code so that the 'chunks' are concatenated into the file without any missed bytes, out-of-register shifts in the byte order, or blank lines.
So here goes:
Public Function RecordsetToCSV(ByRef rst As ADODB.Recordset, _
ByRef OutputFile As String, _
Optional ByRef FieldList As Variant, _
Optional ByVal CoerceText As Boolean = True, _
Optional ByVal CleanupText As Boolean = True _
) As Long
' Output a recordset to a csv file and returns the row count.
' If the output file is locked, or specified in an inaccessible location, the
' 'ByRef' OutputFile parameter becomes a file in the user's local temp folder
' You can supply your own field list. This isn't a substituted file header of
' aliased field names: it is a subset of the field names, which ADO will read
' selectively from the recordset. Each item in the list matches a named field
' CoerceText=TRUE will encapsulate all items, numeric or not, in quote marks.
' CleanupText=TRUE strips quotes and linefeeds from the data: FALSE is faster
' You should only set them FALSE if you're confident that the data is 'clean'
' with no quote marks, commas or line breaks in any unencapsulated text field
' This code handles unicode, and outputs a file that can be read by Microsoft
' ODBC and OLEDB database drivers by removing the Byte Order Marker.
On Error Resume Next
' Coding note: we're not doing any string-handling in VBA.Strings: allocating
' deallocating and (especially!) concatenating are SLOW. We are using the VBA
' Join and Split functions ONLY. Feel free to optimise further by declaring a
' faster set of string functions from the Kernel if you want to.
'
' Other optimisations: type pun. Byte Arrays are interchangeable with strings
' Some of our loops through these arrays have a 'step' of 2. This optimises a
' search-and-replace for ANSI chars in an array of 2-byte unicodes. Note that
' it's only used to remove known ANSI 'Latin' characters with a 'low' byte of
' zero: any other use of the two-byte 'step' will fail on non-Latin unicodes.
' ** THIS CODE IS IN THE PUBLIC DOMAIN **
' Nigel Heffernan Excellerando.Blogspot.com
Const FETCH_ROWS As Long = 4096
Dim COMMA As String * 1
Dim BLANK As String * 4
Dim EOROW As String * 2
COMMA = ChrW$(44)
BLANK = ChrW$(13) & ChrW$(10) & ChrW$(13) & ChrW$(10)
EOROW = ChrW$(13) & ChrW$(10)
Dim FetchArray As Variant
Dim i As Long ' i for rows in the output file, records in the recordset
Dim j As Long ' j for columns in the output file, fields in the recordset
Dim k As Long ' k for all other loops: bytes in individual data items
Dim i_Offset As Long
Dim i_LBound As Long
Dim i_UBound As Long
Dim j_LBound As Long
Dim j_UBound As Long
Dim k_lBound As Long
Dim k_uBound As Long
Dim hndFile As Long
Dim varField As Variant
Dim iRowCount As Long
Dim arrBytes() As Byte
Dim arrTemp1() As String
Dim arrTemp2() As String
Dim arrTemp3(0 To 2) As String
Dim boolNumeric As Boolean
Dim strHeader As String
Dim arrHeader() As Byte
Dim strFile As String
Dim strPath As String
Dim strExtn As String
strFile = FileName(OutputFile)
strPath = FilePath(OutputFile)
strExtn = FileExtension(strFile)
If rst Is Nothing Then Exit Function
If rst.State <> 1 Then Exit Function
If strExtn = "" Then
strExtn = ".csv"
End If
With FSO
If strFile = "" Then
strFile = .GetTempName
strFile = Left(strFile, Len(strFile) - Len(".tmp"))
strFile = strFile & strExtn
End If
If strPath = "" Then
strPath = TempSQLFolder
End If
If Right(strPath, 1) <> "\" Then
strPath = strPath & "\"
End If
strExtn = FileExtension(strFile)
If strExtn = "" Then
strExtn = ".csv"
strFile = strFile & strExtn
End If
OutputFile = strPath & strFile
End With
If FileName(OutputFile) <> "" Then
If Len(VBA.FileSystem.Dir(OutputFile, vbNormal)) <> 0 Then
Err.Clear
VBA.FileSystem.Kill OutputFile ' do it now, and reduce wait for deletion
If Err.Number = 70 Then ' permission denied: change the output file name
OutputFile = FileStripExtension(OutputFile) & "_" & FileStripExtension(FSO.GetTempName) & FileExtension(OutputFile)
End If
End If
End If
' ChrW$() gives a 2-byte 'Wide' char. This coerces all subsequent operations to UTF16
arrTemp3(0) = ChrW$(34) ' Encapsulating quote
arrTemp3(1) = vbNullString ' The field value will go here
arrTemp3(2) = ChrW$(34) ' Encapsulating quote
If rst.EOF And rst.BOF Then
FetchArray = Empty
ElseIf rst.EOF Then
rst.MoveFirst
End If
' An empty recordset must still write a header row of field names: we put this in the
' output buffer and write it to the file before we start looping through the records.
ReDim FetchArray(0 To rst.Fields.Count, 0 To 0)
i_LBound = 0
i_UBound = 0
If IsMissing(FieldList) Then
For j = LBound(FetchArray, 1) To UBound(FetchArray, 1) - 1 Step 1
FetchArray(j, i_UBound) = rst.Fields(j).Name
Next j
Else
j = 0
For Each varField In FieldList
j_UBound = j_UBound + 1
Next varField
ReDim arrTemp2(j_LBound To j_UBound)
For Each varField In FieldList
FetchArray(j, i_UBound) = CStr(varField)
j = j + 1
Next varField
End If
ReDim arrTemp1(i_LBound To i_UBound) ' arrTemp1 is the rowset we write to file
ReDim arrTemp2(j_LBound To j_UBound) ' arrTemp2 represents a single record
Do Until IsEmpty(FetchArray)
i_LBound = LBound(FetchArray, 2)
i_UBound = UBound(FetchArray, 2)
j_LBound = LBound(FetchArray, 1)
j_UBound = UBound(FetchArray, 1)
If UBound(arrTemp1) <> i_UBound + 1 Then
ReDim arrTemp1(i_LBound To i_UBound + 1)
arrTemp1(i_UBound + 1) = vbNullString ' The 'Join' operation will insert a trailing row
End If ' delimiter here (Not required by the last chunk)
If UBound(arrTemp2) <> j_UBound Then
ReDim arrTemp2(j_LBound To j_UBound)
End If
' Data body. This is heavily optimised to avoid VBA String functions with allocations
For i = i_LBound To i_UBound Step 1
' If this is confusing... Were you expecting FetchArray(i,j)? i for row, j for column?
' FetchArray comes from RecordSet.GetRows(), which returns a TRANSPOSED array: i and j
' are still the field and record ordinals, row(i) and column(j) in the output file.
For j = j_LBound To j_UBound
If IsNull(FetchArray(j, i)) Then
arrTemp2(j) = ""
Else
arrTemp2(j) = FetchArray(j, i) ' confused? see he note above
End If
If CleanupText Or (i_UBound = 0) Then ' (i_UBound=0): always clean up field names
arrBytes = arrTemp2(j) ' Integer arithmetic is faster than string-handling for
' this: all VBA string operations require an allocation
For k = LBound(arrBytes) To UBound(arrBytes) Step 2
Select Case arrBytes(k)
Case 10, 13, 9, 160
If arrBytes(k + 1) = 0 Then
arrBytes(k) = 32 ' replaces CR, LF, Tab, and non-breaking
End If ' spaces with the standard ANSI space
Case 44
If Not CoerceText Then
If arrBytes(k + 1) = 0 Then
arrBytes(k) = 32 ' replace comma with the ANSI space
End If
End If
Case 34
If arrBytes(k + 1) = 0 Then
arrBytes(k) = 39 ' replaces double-quote with single quote
End If
End Select
Next k
arrTemp2(j) = arrTemp2(j)
End If ' cleanup
If CoerceText Then ' encapsulate all fields in quotes, numeric or not
arrTemp3(1) = arrTemp2(j)
arrTemp2(j) = Join$(arrTemp3, vbNullString)
ElseIf (i = 0) And (i = i_UBound) Then ' always encapsulate field names
arrTemp3(1) = arrTemp2(j)
arrTemp2(j) = Join$(arrTemp3, vbNullString)
Else ' selective encapsulation, leaving numeric fields unencapsulated:
' we *could* do this by reading the ADODB field types: but that's
' slower, and you may be 'caught out' by provider-specific types.
arrBytes = arrTemp2(j)
boolNumeric = True
For k = LBound(arrBytes) To UBound(arrBytes) Step 2
If arrBytes(k) < 43 Or arrBytes(k) > 57 Then
If arrBytes(k) <> 69 Then
boolNumeric = False
Exit For
Else
If k > UBound(arrBytes) - 5 Then
boolNumeric = False
Exit For
ElseIf arrBytes(k + 2) = 45 Then
' detect "1.234E-05"
ElseIf arrBytes(k + 2) = 43 Then
' detect "1.234E+05"
Else
boolNumeric = False
Exit For
End If
End If
End If
Next k
If boolNumeric Then
For k = 1 + LBound(arrBytes) To UBound(arrBytes) Step 2
If arrBytes(k) <> 0 Then
boolNumeric = False
Exit For
End If
Next k
End If
arrBytes = vbNullString
If Not boolNumeric Then ' text field, encapsulate it
arrTemp3(1) = arrTemp2(j)
arrTemp2(j) = Join(arrTemp3, vbNullString)
End If
End If ' CoerceText
Next j
arrTemp1(i) = Join(arrTemp2, COMMA)
Next i
iRowCount = iRowCount + i - 2
' **** WHY WE 'PUT' A BYTE ARRAY INSTEAD OF A VBA STRING VARIABLE **** ****
'
' Put #hndFile, , StrConv(Join(arrTemp1, EOROW), vbUnicode)
' Put #hndFile, , Join(arrTemp1, EOROW)
'
' If you pass unicode, Wide or UTF-16 string variables to PUT, it prepends a
' Unicode Byte Order Mark to the data which, when written to your file, will
' render the field names illegible to Microsoft's JET ODBC and ACE-OLEDB SQL
' drivers (which can actually read unicode field names, if the helpful label
' isn't in the way). The primeval 'PUT' statement writes a Byte array as-is.
'
' **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
arrBytes = Join$(arrTemp1, vbCrLf)
If hndFile = 0 Then
i_Offset = 1
If Len(Dir(OutputFile)) > 0 Then
VBA.FileSystem.Kill OutputFile
End If
WaitForFileDeletion OutputFile
hndFile = FreeFile
Open OutputFile For Binary Access Write As #hndFile
End If
Put #hndFile, i_Offset, arrBytes
i_Offset = i_Offset + 1 + UBound(arrBytes)
Erase arrBytes
If rst.EOF Then
Erase FetchArray
FetchArray = Empty
Else
If IsMissing(FieldList) Then
FetchArray = rst.GetRows(FETCH_ROWS)
Else
FetchArray = rst.GetRows(FETCH_ROWS, , FieldList)
End If
End If
Loop ' until isempty(FetchArray)
If iRowCount < 1 Then '
iRowCount = 0 ' Row Count excludes the header
End If
RecordsetToCSV = iRowCount
ExitSub:
On Error Resume Next
If hndFile <> 0 Then
Close #hndFile
End If
Erase arrBytes
Erase arrTemp1
Erase arrTemp2
Exit Function
ErrSub:
Resume ExitSub
End Function
Public Function FilePath(Path As String) As String
' Strip the filename from a path, leaving only the path to the folder
' The last char of this path will be the backslash
' This does not check for the existence or accessibility of the file:
' all we're doing here is string-handling
Dim strPath As String
Dim arrPath() As String
Const BACKSLASH As String * 1 = "\"
strPath = Trim(Path)
If strPath = "" Then Exit Function
If Right$(strPath, 1) = BACKSLASH Then Exit Function
arrPath = Split(strPath, BACKSLASH)
If UBound(arrPath) = 0 Then ' does not contain "\"
FilePath = ""
Else
arrPath(UBound(arrPath)) = vbNullString
FilePath = Join$(arrPath, BACKSLASH)
End If
Erase arrPath
End Function
Public Function FileName(Path As String) As String
' Strip the folder and path from a file's path string, leaving only the file name
' This does not check for the existence or accessibility of the file:
' all we're doing here is string-handling
Dim strPath As String
Dim arrPath() As String
Const BACKSLASH As String * 1 = "\"
strPath = Trim(Path)
If strPath = "" Then Exit Function
If Right$(strPath, 1) = BACKSLASH Then Exit Function
arrPath = Split(strPath, BACKSLASH)
If UBound(arrPath) = 0 Then ' does not contain "\"
FileName = Path
Else
FileName = arrPath(UBound(arrPath))
End If
Erase arrPath
End Function
Public Function FileExtension(Path As String) As String
' Return the extension of the file
' This is just string-handling: no file or path validation is attempted
' The file extension is deemed to be whatever comes after the final '.'
' The extension is returned with the dot, eg: ".txt" not "txt"
' If no extension is detected, FileExtension returns an empty string
Dim strFile As String
Dim arrFile() As String
Const DOT_EXT As String * 1 = "."
strFile = FileName(Path)
strFile = Trim(strFile)
If strFile = "" Then Exit Function
If Right$(strFile, 1) = DOT_EXT Then Exit Function
arrFile = Split(strFile, DOT_EXT)
If UBound(arrFile) = 0 Then ' does not contain "\"
FileExtension = vbNullString
Else
FileExtension = arrFile(UBound(arrFile))
FileExtension = Trim(FileExtension)
If Len(FileExtension) > 0 Then
FileExtension = DOT_EXT & FileExtension
End If
End If
Erase arrFile
End Function
Public Function FileStripExtension(Path As String) As String
' Return the filename, with the extension removed
' This is just string-handling: no file validation is attempted
' The file extension is deemed to be whatever comes after the final '.'
' Both the dot and the extension are removed
Dim strFile As String
Dim arrFile() As String
Const DOT_EXT As String * 1 = "."
strFile = FileName(Path)
If strFile = "" Then Exit Function
If Right$(strFile, 1) = DOT_EXT Then Exit Function
strFile = Trim(strFile)
arrFile = Split(strFile, DOT_EXT)
If UBound(arrFile) = 0 Then ' does not contain "\"
FileStripExtension = vbNullString
Else
ReDim Preserve arrFile(LBound(arrFile) To UBound(arrFile) - 1)
FileStripExtension = Join$(arrFile, DOT_EXT)
End If
Erase arrFile
End Function
You'll also need the three path-and-file-name utility functions, if you don't have your own versions already:
FileName()
FilePath()
FileStripExtension()
There's room for improvement in the string-encapsulation logic: the correct approach is to look up the recordset's field types and apply quote marks accordingly, and it may well turn out to be faster than my clunky byte-counting.
However, my approach is all about the file consumers and what they expect to see; and that doesn't always line up with what they ought to accept.
If you succeed in coding a faster and more robust version do, please, let me know: if I'm asked to, I may well code up encapsulation by field type myself.
just thought I would toss in; macros offer this feature - and it is quite simple to set up;
select the export macro, select the query to export, select the format.... if you leave the destination selector blank it will launch the standard Windows file picker....
after a decade+ of coding in vba - macros have won me over for this particular function.....
I have a bunch of text files that I need to import into MS Access (thousands) - can use 2007 or 2010. The text files have categories that are identified in square brackets and have relevant data between the categories - for example:
[Location]Tenessee[Location][Model]042200[Model][PartNo]113342A69447B6[PartNo].
I need to capture both the categories and the data between them and import them into Access - the categories to one table, the data to another. There are hundreds of these categories in a single file and the text file has no structure - they are all run together as in the example above. The categories in the brackets are the only clear delimiters.
Through research on the web I have come up with a script for VBS (I am not locked into VBS, willing to use VBA or another method), but when I run it, I am getting a VBS info window with nothing displaying in it. Any advice or guidance would be most gratefully appreciated (I do not tend to use VBS and VBA) and I thank you.
The Script:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Users\testGuy\Documents\dmc_db_test\DMC-TEST-A-00-00-00-00A-022A-D_000 - Copy01.txt", ForReading)
strContents = objFile.ReadAll
objFile.Close
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.Global = True
objRegEx.Pattern = "\[.{0,}\]"
Set colMatches = objRegEx.Execute(strContents)
If colMatches.Count > 0 Then
For Each strMatch in colMatches
strMatches = strMatches & strMatch.Value
Next
End If
strMatches = Replace(strMatches, "]", vbCrlf)
strMatches = Replace(strMatches, "[", "")
Wscript.Echo strMatches
Regular expressions are wonderful things, but in your case it looks like they might be overkill. The following code uses plain old InStr() to find the [Tags] and parses the file(s) out to a single CSV file. That is, for input files
testfile1.txt:
[Location]Tennessee[Location][Model]042200[Model][PartNo]113342A69447B6[PartNo]
[Location]Mississippi[Location][Model]042200[Model][SerialNo]3212333222355[SerialNo]
and testfile2.txt:
[Location]Missouri[Location][Model]042200[Model][PartNo]AAABBBCCC111222333[PartNo]
...the code will write the following output file...
"FileName","LineNumber","ItemNumber","FieldName","FieldValue"
"testfile1.txt",1,1,"Location","Tennessee"
"testfile1.txt",1,2,"Model","042200"
"testfile1.txt",1,3,"PartNo","113342A69447B6"
"testfile1.txt",2,1,"Location","Mississippi"
"testfile1.txt",2,2,"Model","042200"
"testfile1.txt",2,3,"SerialNo","3212333222355"
"testfile2.txt",1,1,"Location","Missouri"
"testfile2.txt",1,2,"Model","042200"
"testfile2.txt",1,3,"PartNo","AAABBBCCC111222333"
...which you can then import into Access (or whatever) and proceed from there. This is VBA code, but it could easily be tweaked to run as a VBScript.
Sub ParseSomeFiles()
Const InFolder = "C:\__tmp\parse\in\"
Const OutFile = "C:\__tmp\parse\out.csv"
Dim fso As FileSystemObject, f As File, tsIn As TextStream, tsOut As TextStream
Dim s As String, Lines As Long, Items As Long, i As Long
Set fso = New FileSystemObject
Set tsOut = fso.CreateTextFile(OutFile, True)
tsOut.WriteLine """FileName"",""LineNumber"",""ItemNumber"",""FieldName"",""FieldValue"""
For Each f In fso.GetFolder(InFolder).Files
Debug.Print "Parsing """ & f.Name & """..."
Set tsIn = f.OpenAsTextStream(ForReading)
Lines = 0
Do While Not tsIn.AtEndOfStream
s = Trim(tsIn.ReadLine)
Lines = Lines + 1
Items = 0
Do While Len(s) > 0
Items = Items + 1
tsOut.Write """" & f.Name & """," & Lines & "," & Items
i = InStr(1, s, "]", vbBinaryCompare)
' write out FieldName
tsOut.Write ",""" & Replace(Mid(s, 2, i - 2), """", """""", 1, -1, vbBinaryCompare) & """"
s = Mid(s, i + 1)
i = InStr(1, s, "[", vbBinaryCompare)
' write out FieldValue
tsOut.Write ",""" & Replace(Mid(s, 1, i - 1), """", """""", 1, -1, vbBinaryCompare) & """"
s = Mid(s, i)
i = InStr(1, s, "]", vbBinaryCompare)
' (no need to write out ending FieldName tag)
s = Mid(s, i + 1)
tsOut.WriteLine
Loop
Loop
tsIn.Close
Set tsIn = Nothing
Next
Set f = Nothing
tsOut.Close
Set tsOut = Nothing
Set fso = Nothing
Debug.Print "Done."
End Sub
Below is my VBS code. I will admit, I am extremely new to this since I was give this as a job at work. I need to create a logon/logoff script that will capture certain information and store it in a csv file. I am able to get the information and store it in the csv file, but when I try to do it again, I want it to create another row and store the updated information there. I keep getting these asian characters. What seems to be the problem.
This is what I get on the second time I click my log off script:
潙牵琠硥⁴潧獥栠牥
' ******************** Log Off Script **********************************
'Script to write Logoff Data Username, Computername to eventlog.
Dim objShell, WshNetwork, PCName, UserName, strMessage, strContents, logDate, logTime
Dim strQuery, objWMIService, colItems, strIP, rowCount
' Constants for type of event log entry
const EVENTLOG_AUDIT_SUCCESS = 8
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objShell = CreateObject("WScript.Shell")
Set WshNetwork = WScript.CreateObject("WScript.Network")
logDate = Date()
logTime = Time()
PCName = WshNetwork.ComputerName
UserName = WshNetwork.UserName
strMessage = "Logoff Event Data" & "PC Name: " & PCName & "Username: " & UserName & "Date: " & logDate & "Time: " & logTime
If (objFSO.FileExists("test.csv")) Then
WScript.Echo("File exists!")
dim filesys, filetxt
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Set filesys = CreateObject("Scripting.FileSystemObject")
Set filetxt = filesys.OpenTextFile("test.csv", ForAppending, True)
filetxt.WriteLine(vbNewline & "Your text goes here.")
filetxt.Close
Else
rowCount = rowCount + 1
WScript.Echo("File does not exist! File Created!")
Set csvFile = objFSO.CreateTextFile("test.csv", _
ForWriting, True)
objShell.LogEvent EVENTLOG_AUDIT_SUCCESS, strMessage
csvFile.Write strMessage
csvFile.Writeline
End If
WScript.Quit
When in doubt, read the documentation. You're creating the file in Unicode format (3rd argument of CreateTextFile set to True):
Set csvFile = objFSO.CreateTextFile("test.csv", _
ForWriting, True)
but you open an existing (Unicode) file in ASCII format (4th argument of OpenTextFile not specified):
Set filetxt = filesys.OpenTextFile("test.csv", ForAppending, True)
Either open a file in Unicode format all of the time, or never.
Also, it's unnecessary to create more than one FileSystemObject instance. Just use the one you created at the beginning throughout the entire script. You can also use the same variable for the text stream object.
If you want to use Unicode format you need to change the above two lines into this:
Set csvFile = objFSO.CreateTextFile("test.csv", False, True)
...
Set csvFile = objFSO.OpenTextFile("test.csv", ForAppending, False, True)
otherwise into this:
Set csvFile = objFSO.CreateTextFile("test.csv")
...
Set csvFile = objFSO.OpenTextFile("test.csv", ForAppending)
I have a VBScript code snippet which converts my xls and xlsx files into csv files. However, I want each cell to be separated by a semicolon rather than a comma. On my computer, the list separator is set to semicolon instead of comma so when I open up an excel window and do save as csv, it separates by semicolon. However, my VBScript produces a csv file separated by commas. I found the code snippet online as I do not really know VBScript (I'm mainly a Java Programmer) that well. How can I change the code snippet to separate the csv files by semicolon rather than by comma?
if WScript.Arguments.Count < 2 Then
WScript.Echo "Error! Please specify the source path and the destination. Usage: XlsToCsv SourcePath.xls Destination.csv"
Wscript.Quit
End If
Dim oExcel
Set oExcel = CreateObject("Excel.Application")
Dim oBook
Set oBook = oExcel.Workbooks.Open(Wscript.Arguments.Item(0))
oBook.SaveAs WScript.Arguments.Item(1), 6
oBook.Close False
oExcel.Quit
WScript.Echo "Done"
you can keep your original script, only need to give a parameter to indicate local setting must apply. This saves my CSV with a ; separator
if WScript.Arguments.Count < 2 Then
WScript.Echo "Error! Please specify the source path and the destination. Usage: XlsToCsv SourcePath.xls Destination.csv"
Wscript.Quit
End If
Dim oExcel
Set oExcel = CreateObject("Excel.Application")
oExcel.DisplayAlerts = FALSE 'to avoid prompts
Dim oBook, local
Set oBook = oExcel.Workbooks.Open(Wscript.Arguments.Item(0))
local = true
call oBook.SaveAs(WScript.Arguments.Item(1), 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, local) 'this changed
oBook.Close False
oExcel.Quit
WScript.Echo "Done"
The use of a comma in delimited text files finds its roots in the regional settings. While the comma is standard in the US, other countries such as Germany use the semicolon instead. You can change the List Separator value in the Regional and Language settings and then choose CSV (Comma delimited) (.csv) from Excel's Save As window. The resulting file will be delimited by whatever value is in the system settings. This script changes default List Separator setting. Then it opens the specified spreadsheet and resaves it. It reverts the system setting to its previous value before finishing.
It accepts two command line parameters. The first is the input spreadsheet; the second is the output filename for the exported file.
strDelimiter = ";"
strSystemDelimiter = "" ' This will be used to store the current sytem value
Const HKEY_CURRENT_USER = &H80000001
' Get the current List Separator (Regional Settings) from the registry
strKeyPath = "Control Panel\International"
strValueName = "sList"
strComputer = "."
Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
objRegistry.GetStringValue HKEY_CURRENT_USER, strKeyPath, strValueName, strSystemDelimiter
' Set it temporarily to our custom delimiter
objRegistry.SetStringValue HKEY_CURRENT_USER, strKeyPath, strValueName, strDelimiter
' Open spreadsheet with Excel and save it in a text delimited format
Const xlCSV = 6
Set objExcel = CreateObject("Excel.Application")
Set objWorkbook = objExcel.Workbooks.Open(WScript.Arguments.Item(0))
objWorkbook.SaveAs WScript.Arguments.Item(1), xlCSV
objWorkbook.Close vbFalse ' Prevent duplicate Save dialog
objExcel.Quit
' Reset the system setting to its original value
objRegistry.SetStringValue HKEY_CURRENT_USER, strKeyPath, strValueName, strSystemDelimiter
After some testing, it seems that this only works through Excel's Save As dialog and not through command-line or automation. I've changed the script a little to make the Excel window visible and use shortcuts keys to open the Save As dialog through the Excel interface. This should do the trick. It worked for me on Vista x64 with Excel 2007. I hope this works for you.
strDelimiter = ";"
strSystemDelimiter = "" ' This will be used to store the current sytem value
Const HKEY_CURRENT_USER = &H80000001
' Get the current List Separator (Regional Settings) from the registry
strKeyPath = "Control Panel\International"
strValueName = "sList"
strComputer = "."
Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
objRegistry.GetStringValue HKEY_CURRENT_USER, strKeyPath, strValueName, strSystemDelimiter
' Set it temporarily to our custom delimiter
objRegistry.SetStringValue HKEY_CURRENT_USER, strKeyPath, strValueName, strDelimiter
' Open spreadsheet with Excel and save it in a text delimited format
Const xlCSV = 6
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = vbTrue
Set objWorkbook = objExcel.Workbooks.Open(WScript.Arguments.Item(0))
WScript.Sleep 500 ' Delay to make sure the Excel workbook is open
strWorkbookName = objExcel.ActiveWorkbook.Name
strTitlebar = strWorkbookName
Set WshShell = CreateObject("WScript.Shell")
WshShell.AppActivate strTitlebar ' Make the workbook active so it receives the keystrokes
WshShell.SendKeys "%fa" ' Keyboard shortcuts for the Save As dialog
WScript.Sleep 500
WshShell.SendKeys "%tc{ENTER}" ' Change the Save As type to CSV
If WScript.Arguments.Count > 1 Then
WshShell.SendKeys "+{TAB}" & WScript.Arguments.Item(1)
WScript.Sleep 500
End If ' This If block changes the save name if one was provided
WshShell.SendKeys "{ENTER}" ' Save the file
WScript.Sleep 500
WshShell.SendKeys "{ENTER}" ' Dismiss the CSV warning dialog
Set WshShell = Nothing
objWorkbook.Close vbFalse ' Prevent duplicate Save dialog
objExcel.Quit
' Reset the system setting to its original value
objRegistry.SetStringValue HKEY_CURRENT_USER, strKeyPath, strValueName, strSystemDelimiter
The Function SaveAs is defined so :
.SaveAs(FileName, FileFormat, Password, WriteResPassword, ReadOnlyRecommended, CreateBackup, AccessMode, ConflictResolution, AddToMru, TextCodepage, TextVisualLayout, Local)
Thas is, to use the semicolon (if your regional language option are correctly set)
ExcelObj.Workbooks(1).SaveAs csvFile, 6,,,,,,,,,,True
You can reopen the file with the FSO object, then do a Replace() on the comma character.
Const OpenAsDefault = -2
Const FailIfNotExist = 0
Const ForReading = 1
Const ForWriting = 2
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set fCSVFile = _
oFSO.OpenTextFile("C:\path\file.csv", ForReading, FailIfNotExist, OpenAsDefault)
sFileContents = fCSVFile.ReadAll
fCSVFile.Close
sFileContents = Replace(sFileContents, ",",";"))
Set fCSVFile = oFSO.OpenTextFile("C:\path\file.csv", ForWriting, True)
fCSVFile.Write(sFileContents)
fCSVFile.Close
I changed the parameter to true, and worked for me.
if WScript.Arguments.Count < 2 Then
WScript.Echo "Erro! Especifique origem e destino. Exemplo: XlsToCsv SourcePath.xls Destination.csv"
Wscript.Quit
End If
Dim oExcel
Set oExcel = CreateObject("Excel.Application")
Dim oBook
Set oBook = oExcel.Workbooks.Open(Wscript.Arguments.Item(0))
call oBook.SaveAs(WScript.Arguments.Item(1), 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, true) --CHANGED
oBook.Close False
oExcel.Quit