Parsing text files from Excel with string manipulation - excel

Below is an example of a parsing program. It takes text from a text file and parses the data using string manipulation, and a couple loops:
Dim myFile As String
Dim text As String
Dim textline As String
Dim cstAct as integer
Dim actOpe as integer
Dim cusNam as integer
Dim act as integer
Dim reg as integer
myFile = "put file patch to text file here"
myFile = Application.GetOpenFilename()
Here is the do loop that I would like to pause once it reaches line 3 (the next account record)
Do Until EOF(1)
Line Input #1, textline
text = text & textline
Loop
cusAct = InStr(text, "ACCOUNT ")
actOpe = InStr(text, "ACCOUNT OPEN:")
reg = InStr(text, "REGION:")
cusNam = InStr(text, "CUSTOMER NAME:")
This is the for...loop I wish to execute once the do...loop stops or 'pauses once it reaches the next record
For i = 2 To ThisWorkbook.Worksheets("b2").Range("a65536").End(xlUp).Row
ThisWorkbook.Worksheets("name").Range("a" & i).Value = Mid(text, cstAct + 6, 9)
ThisWorkbook.Worksheets("name").Range("b" & i).Value = Mid(text, actOpe + 13, 27)
ThisWorkbook.Worksheets("name").Range("c" & i).Value = Mid(text, reg + 6, 9)
ThisWorkbook.Worksheets("name").Range("d" & i).Value = Mid(text, cusNam + 20, 19)
This is where I want to resume the 'do...loop' so that new the sub strings (ie 987654321 would be the new substring that results from Mid(text, cstAct + 6, 9)) of its respective parent string (ie ACCOUNT) refresh so to speak
otherwise, lines 1 and 2 will just loop over and over again.
next i
Below is an example of the sample text file:
ACCOUNT ABCDEF12
ACCOUNT OPEN: 05/10/15 ACT TYPE: PREMIUM
CUSTOMER NAME: JOHN B. SMITH CSA REP: 154983
CUSTOMER ADDRESS: 123 SOMEWHERE DRIVE SOMETHING HERE:
LAST ORDER: 06/24/2011 COUNTRY CODE: UNITED STATES
INVOICE #: 123456789 STATE CODE: CALIFORNIA
LAST MAINTENANCE: 01/02/15 COUNTY CODE: UNCODED
SOME INDICATOR: NO COMPLAINTS: NO IPM IND: DATAPREP/PERF4
SOME INDICATOR: NO STATUS: NONE AUTO RENEW: YES
SOMETHING HERE: NO
SOMETHING HERE: ABC IND:
SOMETHING HERE: 2 ABC ASSET NO: T
ACCOUNT ZXYFDG13
ACCOUNT OPEN: 05/10/15 ACT TYPE: PREMIUM
CUSTOMER NAME: JANE B. SMITH CSA REP: 154983
CUSTOMER ADDRESS: 123 SOMEWHERE DRIVE SOMETHING HERE:
LAST ORDER: 06/24/2011 COUNTRY CODE: UNITED STATES
INVOICE #: 123456789 STATE CODE: CALIFORNIA
LAST MAINTENANCE: 01/02/15 COUNTY CODE: UNCODED
SOME INDICATOR: NO COMPLAINTS: NO IPM IND: DATAPREP/PERF4
SOME INDICATOR: NO STATUS: NONE AUTO RENEW: YES
SOMETHING HERE: NO
SOMETHING HERE: ABC IND: NO
SOMETHING HERE: 2 REGION: NE
without adjusting the above code structure, output in excel will look like this:
A B C D
ROW 1 123456789 00/00/0000 NY JON SMITH
ROW 2 123456789 00/00/0000 NY JON SMITH
I am trying to get it to look like this:
A B C D
ROW 1 123456789 00/00/0000 NY JON SMITH
ROW 2 987654321 00/00/0000 FL JANE SMITH
Any thoughts on how to best do this?

If you know the literal structure of each "record type", then you can declare them as VBA User Defined Type structures for reading (and writing). Further, it looks like you can simplify your efforts with a slightly different code design and improve your error handling.
Consider how I would approach this problem using UDFs, which makes the code so much more readable and therefore, maintainable:-
'Always set this to ensure you have all variables declared
Option Explicit
'User Defined Types for each record format
Private Type AccountInfoType
OpenText As String * 18 'Absorb all text and prefixes up to data
OpenDate As String * 8 'Contains the data
AccTypeText As String * 24 'Absorb all text and prefixes up to data
AccType As String * 7 'Contains the data
'Add additional fields here
CRLF As String * 2 'CR/LF character
End Type
Private Type CustomerNameType
NameText As String * 18 'Absorb all text and prefixes up to data
Name As String * 20 'Contains the data
CsaRepText As String * 12 'Absorb all text and prefixes up to data
CsaRep As String * 6 'Contains the data
'Add additional fields here
CRLF As String * 2 'CR/LF character
End Type
Private Type AddressType
AddressText As String * 18 'Absorb all text and prefixes up to data
AddressData As String * 20 'Contains the data
SomethingHereText As String * 17 'Absorb remaining text
'Add additional fields here
CRLF As String * 2 'CR/LF character
End Type
Private Type LastOrderType
LastOrderText As String * 18 'Absorb all text and prefixes up to data
LastOrderDate As String * 10 'Contains the data
CountryText As String * 27 'Absorb all text and prefixes up to data
Country As String * 13 'Contains the data
'Add additional fields here
CRLF As String * 2 'CR/LF character
End Type
Private Type InvoiceType
InvoiceText As String * 18 'Absorb all text and prefixes up to data
InvoiceNumber As String * 9 'Contains the data
StateText As String * 28 'Absorb all text and prefixes up to data
State As String * 10 'Contains the data
'Add additional fields here
CRLF As String * 2 'CR/LF character
End Type
Sub ParseFile()
Dim wb As Workbook
Dim ws As Worksheet
Dim row As Long
Dim dataRecord As String
Dim accountNumber As String
Dim accountInfo As AccountInfoType
Dim customerName As CustomerNameType
Dim address As AddressType
Dim lastOrder As LastOrderType
Dim invoice As InvoiceType
Dim myFile As Variant
'Consider using proper error handling
On Error GoTo ParseFileZ
myFile = Application.GetOpenFilename()
If myFile = False Then
'Not a fan of GoTo but better than running the whole method inside if/then block
GoTo ParseFileX
End If
'I started with a new workbook. Change this to open an exsting workbook if desired
Set wb = Application.Workbooks.Add
'Set this handle to your desired worksheet
Set ws = wb.Worksheets(1)
'Set up column headers here. I chose row 3 to allow for a heading in row 1. Choose your own...
ws.Range("A3").Value = "Acc Number"
ws.Range("B3").Value = "Acc Opened"
ws.Range("C3").Value = "Region"
ws.Range("D3").Value = "Name"
'Base output row in the worksheet
row = 3
'Open the file in binary mode so that you can use User Defined Types to read each record
Open CStr(myFile) For Binary As #1
While Not EOF(1)
'Read next record
Input #1, dataRecord
'Find the first record of the next account - otherwise, skip until you get one
If Left(dataRecord, 7) = "ACCOUNT" And Len(dataRecord) = 16 Then
'Found the Account Number record. This is the start of the next account
accountNumber = Mid(dataRecord, 9, 8)
Get #1, , accountInfo 'Read the Account info record
Get #1, , customerName 'Read the Customer Name record
Get #1, , address 'Read the Address record
Get #1, , lastOrder 'Read the Last Order record
Get #1, , invoice 'read the Invoice record
'Ignore the remaining records unless you want to get more data. The "Read Next Record" loop will skip them
'Get the next row number on the output worksheet to write values to
row = row + 1
'Assign the values from the various records
ws.Cells(row, 1).Value = Trim(accountNumber)
ws.Cells(row, 2).Value = Trim(accountInfo.OpenDate)
ws.Cells(row, 3).Value = Trim(invoice.State) '(you talk about "region" but no region in data sample)
ws.Cells(row, 4).Value = Trim(customerName.Name)
'Add more cells for additional records you want to extra fields from here
End If
Wend
'We're finished. Close the file
Close #1
'Resize the cells for readibilty
ws.Cells.EntireColumn.AutoFit
ParseFileX:
'Disable error handling
On Error GoTo 0
'Be a good memory citizen
Set ws = Nothing
Set wb = Nothing
Exit Sub
ParseFileZ:
MsgBox Err.Number & " - " & Err.Description, "Error occurred"
Resume ParseFileX
End Sub

Related

How to create multi-column dictionary and get data from it in VBA Excel

I want to load dictionary table of following structure to an array (dictionary? UDT class?), say "dictCompany", CompanyName is unique key.
CompanyName | PersonName | PersonPhone
ABC Ltd | Nick | +12345
XYZ Co | Alice | +78901
And then I need to find entries by CompanyName and refer to other columns like dictCompany.PersonName or dictCompany.PersonPhone, something like this pseudo-code:
Dim i as Long
Dim dictIndex as Long
For i = 0 To UBound(dictCompany)
If dictCompany(i).CompanyName = "ABC Ltd" Then
dictIndex = i
End If
Next i
debug.print dictCompany(dictIndex).PersonName 'should get "Nick"
debug.print dictCompany(dictIndex).PersonPhone 'should get "+12345"
I don't know what technology to use - array, dictionary, UDT class or whatever VBA has, so I would appreciate even directional answers with keywords for furhter search.
Create a class called clsPerson and in your class place the following:
Public PersonName as String, PersonPhone as String, CompanyName as String
Then in your Module which loads the Dictionary use:
Option Explicit
Public Sub Demo()
Dim dictCompany As Object
Dim Emp As clsPerson
Dim i As Long
Set dictCompany = CreateObject("Scripting.Dictionary")
' Load Data into Dictionary
For i = 2 To 3
Set Emp = New clsPerson
Emp.CompanyName = Range("A" & i).Value2
Emp.PersonName = Range("B" & i).Value2
Emp.PersonPhone = Range("C" & i).Value2
If dictCompany.Exists(i) Then
Set dictCompany(i) = Emp
Else
dictCompany.Add i, Emp
End If
Next i
' Read back
For i = 2 To 3
If dictCompany(i).CompanyName = "ABC Ltd" Then
Debug.Print dictCompany(i).PersonName 'should get "Nick"
Debug.Print dictCompany(i).PersonPhone 'should get "+12345"
Exit For
End If
Next i
End Sub
You could expand on this as well to use the Company Name as your key or use a Collection instead of a Dictionary

Options for parsing a text file to columns

I am looking for some input and possible example for parsing a text file with the following format: (sorry not sure how to retain the formatting of the file in this text)
NAME ID FORMAT SHORT NAME
DESCRIPTION (this field is on the second row an indented by 5 spaces)
The first row (NAME, ID, FORMAT and SHORT NAME) always consist of just one row. The DESCRIPTION text may span multiple rows. In some cases, there is only a first row of NAME, ID, etc. without a corresponding DESCRIPTION row.
Here is an example of how the data looks in the file now:
NAME ID FORMAT SHORT NAME
DESCRIPTION
ABC 01 xx AB
abcdefg
hijklm
nopqrs
DEF 02 xx DE
abcedfg
hijklmnopqrst
GHI 03 xx.x GH
JKL 001 xx JKL
abcdef
ghijk
lmnopq
rstu
vwxyz
I would like to parse out the NAME, ID, FORMAT, SHORT NAME and DESCRIPTION into 5 separate columns in a csv or excel file for additional analysis. I don't care if the DESCRIPTION field is broken across multiple lines but it can also be concatenated into a single longer string.
Hope this all makes sense. Thanks in advance!
Providing the data for NAME,ID,FORMAT and SHORT NAME is aligned
beneath their header word then use those words on the first line
to calculate the start position and length of each field, then split
the lines into fields using Mid(). Join the description lines and write out to
the previous record before a new record is started. For example
Option Explicit
Sub ParseTextFile()
Const INFILE = "c:\temp\testfile.txt"
Const OUTFILE = "c:\temp\testfile.xlsx"
Dim wbOut As Workbook, ws As Worksheet, iRow As Long
Dim txt As String, ff As Integer, i As Integer, desc As String
Dim start(4) As Integer, length(4) As Integer
Dim count As Integer, msg As String
Set wbOut = Workbooks.Add
Set ws = wbOut.Sheets("Sheet1")
ws.Range("A1:E1") = Array("NAME", "ID", "FORMAT", "SHORT NAME", "DESCRIPTION")
ws.Columns("A:E").NumberFormat = "#"
iRow = 1
ff = FreeFile()
Open INFILE For Input As #ff
While Not EOF(ff)
count = count + 1
Line Input #ff, txt
If count = 1 Then
start(1) = InStr(1, txt, "NAME", vbTextCompare)
start(2) = InStr(1, txt, "ID", vbTextCompare)
start(3) = InStr(1, txt, "FORMAT", vbTextCompare)
start(4) = InStr(1, txt, "SHORT NAME", vbTextCompare)
For i = 1 To 3
length(i) = start(i + 1) - start(i)
Next
Else
If Left(txt, 1) = " " Then
desc = desc & Trim(txt) & " "
Else
' save the description from last record
ws.Cells(iRow, 5) = Trim(desc)
desc = ""
' new row
iRow = iRow + 1
length(4) = Len(txt) - start(4) + 1
For i = 1 To 4
ws.Cells(iRow, i) = Mid(txt, start(i), length(i))
Next
End If
End If
Wend
Close #ff
' final description
ws.Cells(iRow, 5) = Trim(desc)
' save result
ws.Columns("A:E").AutoFit
wbOut.Close True, OUTFILE
msg = count & " lines read from " & INFILE & vbCr & _
iRow - 1 & " rows written to " & OUTFILE
MsgBox msg, vbInformation
End Sub

Excel VBA macro to count the number of times a specific word appears according to a date (that would be converted to week number)

So I have column A (contains the word) and column C (contains the date) shown below. The columns are occasionally separated by new headers, such as for "Word" and "Date" and a blank space.
Word Date
BV 12/06/2017
BV 12/06/2017
BV 13/06/2017
BV 13/06/2017
BR 17/07/2017
BR 17/07/2017
BR 24/07/2017
Word Date
BT 30/07/2017
BT 30/07/2017
Word Date
BY 05/08/2017
First the date would be converted in terms of week number into a new column D, such as 12/06/2017 to week 24.
Using something like:
Sub TimeConverter()
Dim I as Long, MaxRow as Long
MaxRow = Range("A" & Rows.count).End(xlUp).Row
For I = 2 to MaxRow
Cells(I, "D").Value = DatePart("ww", Cells(I, "C"), 7)
Next
End Sub
Then I would like the VBA macro code to look through column A and find the number of times a word appears and match with a date on the same week number into a new column B.
Using something like:
=COUNTIF(A:A, "BV")
=COUNTIF(A:A, "BR")
Output
# 4
# 3
Now to then combine them together so that the unique word (column A) counts (column B) can be separated into the corresponding week number (column D).
Desired Output:
BV 4 24
BR 2 29
BR 1 30
BT 2 30
BY 1 31
Any suggestion would be great!
Thank you.
Let's say that with your VBA code you have managed to get something like this as an input:
Then, as mentioned in the comments, you need to implement a dictionary to get something like this:
As you see, the keys of the dictionary is the word + the week number together. Thus BR29 is different than BR30.
Copy the sample input, run the code below and you will get the desired output:
Option Explicit
Public Sub TestMe()
Dim myDict As Object
Dim lngCounter As Long
Dim strKey As String
Dim objKey As Object
Set myDict = CreateObject("Scripting.Dictionary")
For lngCounter = 1 To 14
strKey = Cells(lngCounter, 1) & Cells(lngCounter, 3)
If myDict.exists(strKey) Then
myDict(strKey) = myDict(strKey) + 1
Else
myDict(strKey) = 1
End If
Next lngCounter
For lngCounter = 0 To myDict.Count - 1
Cells(lngCounter + 1, 6) = myDict.Items()(lngCounter)
Cells(lngCounter + 1, 7) = myDict.keys()(lngCounter)
Next lngCounter
End Sub
Then you have to work more to find a way to split the keys from BV24 to BV and 24. You need to find a way to eliminate the zero from the results as well.

Copy clipboard data to array

I have to to copy text, from a web page using Ctrl A + Ctrl C, to use in Excel.
The copied text is about 100 lines with different sizes. Let us say one line has a string of 200 characters and the next one has 500 characters and the third maybe 20 characters.
Is there a way to loop over the clipboard data lines and copy them to an array?
Sample of the copied text (made with Ctrl A Ctrl C in the page):
Note : I removed some Lines
Usernames are XXXXXXXXXXXXXXXXX
DashboardAnalyticsPolicyAdministration
Web Insights
Print View
Start Over
1Select Chart Type
Logs
Apply Filters
2Choose a Timeframe
Custom: 9/1/2015 12:00:00 AM - 9/30/2015 12:00:00 AM
3Select Filters
Add Filter
2.4 TB
2.0 TB
879.9 GB
656.8 GB
472.0 GB
442.4 GB
242.1 GB
213.5 GB
189.3 GB
103.8 GB
Office 365 - SSL Bypass
Professional Services
Streaming Media
Sites everyone
Internet Services
Corporate Marketing
Miscellaneous
Web Search
News and Media
Social Networking
URL CategoryTop 10TransactionsBytes
To follow up on my comment, if you follow the instructions from here add a reference to Microsoft Forms Library 2.0 (under Tools/References in the VBA editor), the following function takes the contents of the clipboard and splits it into lines:
Function ClipToArray() As Variant
Dim clip As New MSForms.DataObject
Dim lines As String
clip.GetFromClipboard
lines = clip.GetText
lines = Replace(lines, vbCr, "")
ClipToArray = Split(lines, vbLf)
End Function
You can test it like this:
Sub test()
Dim A As Variant
Dim i As Long
A = ClipToArray()
For i = LBound(A) To UBound(A)
Debug.Print A(i)
Next i
End Sub
Then I went to this website and copied the poem and then ran test. I got the following output in the immediate window:
Some say the world will end in fire,
Some say in ice.
From what I've tasted of desire
I hold with those who favor fire.
But if it had to perish twice,
I think I know enough of hate
To say that for destruction ice
Is also great
And would suffice.
This worked nicely enough, although you don't have to run many experiments with text copied from the internet before you see that the superficial parsing using split leaves much to be desired.
I made this for those who want to extract 2D information from a copied range.
'Display the content of the clipboard
Sub test()
Dim A As Variant
Dim i As Long
A = ClipToArray()
For i = LBound(A, 1) To UBound(A, 1)
tmp = ""
For j = LBound(A, 2) To UBound(A, 2)
tmp = tmp & A(i, j) & " | "
Next
Debug.Print tmp
Next
End Sub
'Made by LePatay on 2018/12/07
'Extract a 2D array from a copied 2D range
Function ClipToArray()
'Include Tools -> References -> Microsoft Forms 2.0 Object Library
'or you will get a "Compile error: user-defined type not defined"
Dim dataobj As New MSForms.DataObject
Dim array2Dfitted As Variant
Dim cbString As String
'Special characters
quote = """"
tabkey = vbTab
CarrReturn = vbCr
LineFeed = vbLf
'Get the string stored in the clipboard
dataobj.GetFromClipboard
On Error GoTo TheEnd
cbString = dataobj.GetText
On Error GoTo 0
'Note: inside a cell, you only find "vbLf";
'at the end of each row, you find "vbCrLf", which is actually "vbCr & vbLf".
cbString = Replace(cbString, vbCrLf, CarrReturn)
'Length of the string
nbChar = Len(cbString)
'Get the number of rows
nbRows = Application.Max(1, nbChar - Len(Replace(cbString, CarrReturn, "")))
'Get the maximum number of columns possible
nbColumnsMax = nbChar - Len(Replace(cbString, tabkey, "")) + 1
'Initialise a 2D array
Dim array2D As Variant
ReDim array2D(1 To nbRows, 1 To nbColumnsMax)
'Initial position in array2D (1st cell)
curRow = 1
curColumn = 1
'Initialise the actual number of columns
nbColumns = curColumn
'Initialise the previous character
prevChar = ""
'Browse the string
For i = 1 To nbChar
'Boolean "copy the character"
bCopy = True
'Boolean "reinitialise the previous character"
bResetPrev = False
'For each character
curChar = Mid(cbString, i, 1)
Select Case curChar
'If it's a quote
Case quote:
'If the previous character is a quote
If prevChar = quote Then
'Indicates that the previous character must be reinitialised
'(in case of a succession of quotes)
bResetPrev = True
Else
'Indicates the character must not be copied
bCopy = False
End If
'If it's a tab
Case tabkey:
'Indicates the character must not be copied
bCopy = False
'Skip to the next column
curColumn = curColumn + 1
'Updates the actual number of columns
nbColumns = Application.Max(curColumn, nbColumns)
'If it's a carriage return
Case CarrReturn:
'Indicates the character must not be copied
bCopy = False
'If it's not the 1st character
If i > 1 Then
'Skip to the next row
curRow = curRow + 1
curColumn = 1
End If
End Select
'If the character must be copied
If bCopy Then
'Adds the character to the current cell
array2D(curRow, curColumn) = array2D(curRow, curColumn) & curChar
End If
'If the previous character must be reinitialised
If bResetPrev Then
prevChar = ""
Else
'Saves the character
prevChar = curChar
End If
Next
'Create a 2D array with the correct dimensions
ReDim array2Dfitted(1 To nbRows, 1 To nbColumns)
'Copies the data from the big array to the fitted one (no useless columns)
For r = 1 To nbRows
For c = 1 To nbColumns
array2Dfitted(r, c) = array2D(r, c)
Next
Next
TheEnd:
ClipToArray = array2Dfitted
End Function
Remarks:
There is no way to tell if cells are merged).
This code is robust to quotes, successions of quotes, and multiple lines inside a cell.
It has been tested on a French Excel, Win 7 64 bit. The system of quotes / carriage returns / line feeds may differ on your OS.

How do you make an Excel spreadsheet range store a VBA variable?

Okay so I have spent.. I'll just say.. like an hour (sadly I am lying, its been like a whole week) trying to figure this out.
And I can't figure it out godd*mnit >_<
Assume we have a text file in notepad (_A_File_.txt) containing just 4 lines:
ACCOUNT NUMBER: 123456789 '(line 2)
SHORT NAME: JON SMITH '(line 2)
ACCOUNT NUMBER: 987654321 '(line 3)
SHORT NAME: BOB BARKER '(line 4)
Let's assume that within an excel spreadsheet the below:
Range C1 = "actNumberTrim"
Range C2 = "shortNameTrim"
Range C3 = "actNumberTrim"
Range C4 = "shortNameTrim"
Lets also assume the below VBA script:
Sub obey_me_you_stoopit_code_()
Dim actNumber As Integer
Dim shortName As Integer
Dim actNumberTrim As String
Dim shortNameTrim As String
Dim trimArray1 As Variant
Dim myFile As String
Dim text As String
Dim textline As String
actNumber = InStr(text, "ACCOUNT NUMBER: ") 'THERE IS A SPACE BETWEEN : AND "
shortName = InStr(text, "SHORT NAME: ") 'THERE IS A SPACE BETWEEN : AND "
actNumberTrim = Mid(text, actNumber + 16, 10)
shortNameTrim = Mid(text, shortName + 12, 10)
myFile = "C:\Users\Bob\Desktop\_A_File_.txt"
Open myFile For Input As #1
Do Until EOF(1)
Line Input #1, textline
text = text & textline
Loop
trimArray1 = ThisWorkbook.Worksheets("sheet1").Range("C1:C4")
For i = 1 To UBound(trimArray1)
MsgBox trimArray1(i, 1)
Next i
Close #1
End Sub
the output I get is:
[ actNumberTrim ]
[ shortNameTrim ]
[ actNumberTrim ]
[ shortNameTrim ]
How would I go about fixing the above so that values in Ranges C1:C4 are treated as:
Variables and not, say.. pointless text in a cell
So that the output returned is:
[ 123456789 ]
[ JON SMITH ]
[ 987654321 ]
[ BOB BARKER ]
I've tried changing the data types of actNumberTrim and shortNameTrim.
It didn't work
All I got was this:
Run-Time Error: "Your code sucks brah"
Thoughts?
EDIT 1 (Attempt using Microsoft Scripting Runtime)
Dim trimArray1 As Variant
Dim myFile1 As String
Dim text As String
Dim textline As String
Dim myDict As New Dictionary
myFile1 = "C:\Users\BOB\Desktop\_A_File_.txt"
Open myFile1 For Input As #1
Do Until EOF(1)
Line Input #1, textline
text = text & textline
Loop
Close #1
trimArray1 = ThisWorkbook.Worksheets("sheet22").Range("C1:C4")
v = Split(textline, ":") 'where v has been declared to be variant
myDict.Add Trim(v(0)), Trim(v(1))
For i = 1 To UBound(trimArray1)
MsgBox myDict(trimArray1(i, 1))
Next i
[ BLANK ]
[ BOB BARKER ]
[ BLANK ]
[ BOB BARKER ]
Cells C1:C4 Contained:
ACCOUNT NUMBER
SHORT NAME
ACCOUNT NUMBER
SHORT NAME
Not sure what I am doing wrong or if I have overlooked something. How would I use this method for fields that don't have ":" separating them? The version of the text file that I had posted was a little oversimplified. Feedback?
EDIT 2
Below was my original approach
Below is an example of two full records within an example text file:
ACCOUNT ABCDEF12
CUSTOMER NAME: JOHN B. SMITH CSA REP: 154983
ACCOUNT OPEN: 05/10/15
CUSTOMER ADDRESS: 123 SOMEWHERE DRIVE SOMETHING HERE:
LAST ORDER: 06/24/2011 COUNTRY CODE: UNITED STATES
INVOICE #: 123456789 STATE CODE: CALIFORNIA
LAST MAINTENANCE: 01/02/15 COUNTY CODE: UNCODED
SOME INDICATOR: NO COMPLAINTS: NO IPM IND: DATAPREP/PERF4
SOME INDICATOR: NO STATUS: NONE AUTO RENEW: YES
SOMETHING HERE NO
SOMETHING HERE: ABC IND:
SOMETHING HERE 2 ABC ASSET NO: T
ACCOUNT ABCDEF12
CUSTOMER NAME: JOHN B. SMITH CSA REP: 154983
ACCOUNT OPEN: 05/10/15
CUSTOMER ADDRESS: 123 SOMEWHERE DRIVE SOMETHING HERE:
LAST ORDER: 06/24/2011 COUNTRY CODE: UNITED STATES
INVOICE #: 123456789 STATE CODE: CALIFORNIA
LAST MAINTENANCE: 01/02/15 COUNTY CODE: UNCODED
SOME INDICATOR: NO COMPLAINTS: NO IPM IND: DATAPREP/PERF4
SOME INDICATOR: NO STATUS: NONE AUTO RENEW: YES
SOMETHING HERE: NO
SOMETHING HERE: ABC IND:
SOMETHING HERE: 2 ABC ASSET NO: T
Below was the way I had originally coded it:
'
Dim myFile As String
Dim text As String
Dim textline As String
Dim cstAct as integer
Dim actOpe as integer
Dim cusNam as integer
Dim act as integer
Dim reg as integer
myFile = "put file patch to text file here"
myFile = Application.GetOpenFilename()
Do Until EOF(1)
Line Input #1, textline
text = text & textline
Loop
cusAct = InStr(text, "ACCOUNT ")
actOpe = InStr(text, "ACCOUNT OPEN:")
reg = InStr(text, "REGION:")
cusNam = InStr(text, "CUSTOMER NAME:")
For i = 2 To ThisWorkbook.Worksheets("b2").Range("a65536").End(xlUp).Row
ThisWorkbook.Worksheets("name").Range("a" & i).Value = Mid(text, act + 6, 9)
ThisWorkbook.Worksheets("name").Range("b" & i).Value = Mid(text, cstAct + 6, 9)
ThisWorkbook.Worksheets("name").Range("c" & i).Value = Mid(text, actOpe + 13, 27)
ThisWorkbook.Worksheets("name").Range("d" & i).Value = Mid(text, cusNam + 20, 19)
next i
'Format and autofit
For x = 2 To ThisWorkbook.Worksheets("b2").Range("a65536").End(xlUp).Row
Range("a" & x).Value = Application.WorksheetFunction.Clean(trim(Range("a" & x)))
Range("b" & x).Value = Application.WorksheetFunction.Clean(trim(Range("b" & x)))
Range("c" & x).Value = Application.WorksheetFunction.Clean(trim(Range("c" & x)))
'etc etc
next x
You can use strings as keys to a dictionary (which act sort of like variables which are linked to the corresponding values). In the VBA editor under Tools/References, include a reference to Microsoft Scripting Runtime. Then at the top of your code have a line like
Dim myDict As New Dictionary
Then, when you loop through the file and load up the variable textline by the successive lines, don't tack it onto the end of a big variable but instead have the lines
v = Split(textline, ":") 'where v has been declared to be variant
myDict.Add Trim(v(0)), Trim(v(1))
Fix the cells in column C so that they coincide with the actual (trimmed) strings in the text file before the colon. No need to eliminate spaces - "Account Number" is a perfectly valid cell value and a perfectly valid dictionary key. Later, when you are looping through the values drawn from column C, replace
MsgBox trimArray1(i, 1)
By
MsgBox myDict(trimArray1(i, 1))
This should (if I understand your intentions correctly) do what you want it to do.

Resources