reverse engineer vba code excel - vba

I am not a VBA programmer. However, I have the 'unpleasant' task of re-implementing someones VBA code in another language. The VBA code consists of 75 modules which use one massive 'calculation sheet' to store all 'global variables'. So instead of using descriptive variable names, it often uses:
= Worksheets("bla").Cells(100, 75).Value
or
Worksheets("bla").Cells(100, 75).Value =
To make things worse, the 'calculation sheet' also contains some formulas.
Are there any (free) tools which allow you to reverse engineer such code (e.g. create Nassi–Shneiderman diagram, flowcharts)? Thanks.

I think #JulianKnight 's suggestion should work
Building on this, you could:
Copy all the code to a text editor capable of RegEx search/replace (Eg. Notepad++).
Then use the RegEx search/Replace with a search query like:
Worksheets\(\"Bla\"\).Cells\((\d*), (\d*)\).Value
And replace with:
Var_\1_\2
This will convert all the sheet stored values to variable names with row column indices.
Example:
Worksheets("bla").Cells(100, 75).Value To Var_100_75
These variables still need to be initialized.
This may be done by writing a VBA code which simply reads every (relevant) cell in the "Bla" worksheet and writes it out to a text file as a variable initialization code.
Example:
Dim FSO As FileSystemObject
Dim FSOFile As TextStream
Dim FilePath As String
Dim col, row As Integer
FilePath = "c:\WriteTest.txt" ' create a test.txt file or change this
Set FSO = New FileSystemObject
' opens file in write mode
Set FSOFile = FSO.OpenTextFile(FilePath, 2, True)
'loop round adding lines
For col = 1 To Whatever_is_the_column_limit
For row = 1 To Whatever_is_the_row_limit
' Construct the output line
FSOFile.WriteLine ("Var_" & Str(row) & "_" & Str(col) & _
" = " & Str(Worksheets("Bla").Cells(row, col).Value))
Next row
Next col
FSOFile.Close
Obviously you need to correct the output line syntax and variable name structure for whatever other language you need to use.
P.S. If you are not familiar with RegEx (Regular Expressions), you will find a plethora of articles on the web explaining it.

Related

Import semicolon separated CSV file using VBA without rename to txt

This is not a duplicate since I want a solution not constisting in reformatting file to txt:
my intention is to open a csv file using semicolon as delimiter. For that purpose I have used the following code:
Sub prueba2()
Dim sfile As String
Dim wb As Workbook
Dim Path As String
Dim Namefile As String
Path = "V:\evfilesce9i9\apps9\vbe9\dep4\KFTP\KFTP001D_FicherosCeca"
Namefile = "\QryCECARFSECTORIAL0239*.txt"
Set wb = Workbooks.Open(Filename:=Path & Namefile, Delimiter:=";")
End Sub
When I try it, it is opened using commas as delimiter instead of which I have specified (semicolon)
I have read in other questions that this is normal in post 2006 Excel versions, and that the fastest solution is to reformat file to a txt.
This does not fit into my needs because I have to do it without changing format. I don't find any solution.
Could someone help me?
Please see the MS documentation here.
I think you want to use the Format parameter, and not the delimiter parameter.
Try:
Set wb = Workbooks.Open(Filename:=Path & Namefile, Format:=4)
It seems like the Delimiter argument is only used if Format is set to 6, which signifies a custom delimiter character. Semi-colon is a standard delimiter.
Edit:
Hmm... so, this seems to be something that's been tricky in Excel/VBA for a while.
After some more research, the "Format" option may only be used when opening .txt files. Which is why the "reformat file to .txt" is one possible solution.
There are some things that can be done, however.
Excel will handle opening a semicolon delimited file well if the first line of the file is:
sep=;
I know you said you could not reformat the files, but is that something that you can do?
If not, the next things I would suggest would be to either: 1) use the Open Statement to open your file and then write it to a temporary file (perhaps as a .txt), to be reopened with the original Workbooks.Open(Format:=4), or 2) write your own text importer. A sample text importer can be found in this stackoverflow page.
Sub ImportCSVFile(filepath As String)
Dim line As String
Dim arrayOfElements
Dim linenumber As Integer
Dim elementnumber As Integer
Dim element As Variant
linenumber = 0
elementnumber = 0
Open filepath For Input As #1 ' Open file for input
Do While Not EOF(1) ' Loop until end of file
linenumber = linenumber + 1
Line Input #1, line
arrayOfElements = Split(line, ";")
elementnumber = 0
For Each element In arrayOfElements
elementnumber = elementnumber + 1
Cells(linenumber, elementnumber).Value = element
Next
Loop
Close #1 ' Close file.
End Sub

VBA Hyperlinks.Add method prepending local folder to Address

I've been lurking here for a while but this is my first post so let me know if I need to change something. Anyways, here goes:
I'm trying to create a macro that will add hyperlinks to cells in a worksheet. The problem is that after running the macro, I notice that the folder location of my spreadsheet has been prepended to the address that I specified. Is there something I need to do in order to indicate that this is a webpage and not a local file? Excerpt from the macro is below.
Dim IGQ As Range
Dim IGQno As String
Dim IGQno1 As String
For Each IGQ In Range("A2:A10") 'Actual range is much larger
IGQno = IGQ.Value
IGQno1 = Left(IGQ, 1)
Sheets("Cameron DCDA").Hyperlinks.Add Anchor:=IGQ, _
Address:="""http://xxxx""&IGQno1&""xxx""&IGQno&""xxxxx""" 'It's a company website so they probably don't want me to share it
Next
The result is that a hyperlink is created for each cell but it links to file:///C:\Users\John.Doe\Documents\"http://xxxx"&IGQno1&"xxx"&IGQno&"xxxxx"
I've tried using fewer quotation marks in the address since it seems like overkill but I get the compile error "Expected: end of statement"
Do you guys have any suggestions?
Too many quotes
Address:="http://xxxx " & IGQno1 & "xxx" & IGQno & "xxxxx"
Also - be sure to leave a space before your & otherwise it will be interpreted as a variable type suffix:
What are possible suffixes after variable name in VBA?

Import specific lines from text file into Excel spreadsheet using VBA

I'm trying to get a macro setup that will import specific lines from a text file into a Excel spreadsheet. I am currently using the instr function to locate a specific word then read how many letters over I need to import data into the cells.
The reason I am doing it this way is due to the file being over 3500 lines and is not delimited or comma separated in any sense. Some of the data is the same as well which I run into problems with the above tactic.
What I need help with is how to import only like 20 specific lines into the spreadsheet(multiple sheets will be used, but can reuse this code), while using a technique like I mentioned earlier so I can decide where its reading from.
Thanks!
This is what I use constantly. Just replace the file location with your text file and this little blip will read it line by line. Then you can throw logic against a single line and decide what to do with it. Its a good solution when your dealing with data that regex statements can't handle accurately.
Sub LoadSettings()
Dim fso As Scripting.FileSystemObject
Dim F As File
Dim F2 As TextStream
Dim TS As TextStream
Dim lngCount As Long
Set fso = New Scripting.FileSystemObject
fileloc = "Whereyourtextfileislocated"
Set F = fso.GetFile(fileloc)
Set TS = F.OpenAsTextStream(1, -2)
S = ""
Do
RptText = TS.ReadLine
if rpttext = whatever criteria, or even if instr(rpttext,"whateveryouwant") >0 then
do something with rpttext
end if
Loop
End Sub

Create, fill and resize WordTable before inserting it to instance

I am currently working on an application that gets data out of a database and puts it into an open word instance.
Currently it's doing the following steps:
Find open Word Instance (if multiple opened user can select)
Dim oDocs As Word.Document =
WordApplication.Application.Documents(filepath)
Create a table on a bookmark in the Word Instance
Try
WordTable = oDocs.Tables.Add(oDocs.Bookmarks.Item("NameOfBookmark").Range, DataTable.Rows.Count + 1, DataTable.Columns.Count)
Catch ex As Exception
WordTable = oDocs.Tables.Add(oDocs.Application.Selection.Range, DataTable.Rows.Count + 1, DataTable.Columns.Count)
End Try
Fill the table, when it's already in the word instance
Looping for each row and cell -> In this loop is happening a lot, but it's working and doesn't matter for the question so I will not put the code inside here
I know the speed can be so slow because of the stuff happening in the part where it fills the table, but i do not think it's too much.
My Problem is the speed of that. While this all is working fine, it takes years to execute. You can see every Cell being filled in the opened Word-Document. My thoughts for a solution is to find a way to create that WordTable in my VB application and only insert the finished Word-Table into the Word instance, but I can't find a way to do so.
Is there a way to do that? If yes, please tell me how!
TL:DR
Can I completly create and fill and resize a WordTable in my VB application before inserting it into an opened WordInstance? If yes, how?
EDIT
Bibadia just gave the perfect Answer!
I will give you my full working Code now - It only creates a table in the word-instance. You have to format it later in your application.
Dim oDocs As Word.Document = WordApplication.Application.Documents(filepath)
Dim strTable As String = ""
Dim isFirst As Boolean = True
Dim intColumns As Integer = DataTable.Columns.Count
Dim intRows As Integer = DataTable.Rows.Count
For Each column As DataColumn In DataTable.Columns
If Not isFirst Then
strTable &= ";"
End If
strTable &= column.ColumnName
isFirst = False
Next
For Each row As DataRow In DataTable.Rows
For Each column As DataColumn In DataTable.Columns
strTable &= ";" & row.Item(column)
Next
Next
Dim rng As Word.Range
rng = oDocs.Application.Selection.Range
rng.Text = strTable
Dim WordTable As Word.Table = rng.ConvertToTable(NumRows:=intRows + 1, NumColumns:=intColumns, Separator:=Word.WdSeparatorType.wdSeparatorColon)
Three things you could try:
Insert the data as plain text using delimiters that do not appear in your data (e.g. vbTab and vbCr), then use the ConvertToTable method of the range object. You will need to apply formatting after that.
Build the table using WordProcessingML and insert it using the InsertXML method of a Range object. It is up to you how much formatting you attempt to describe using the XML - personally, I would start by inserting the simplest possible table pre-filled with data, then apply formatting using the object model if that is not also too slow.
Use the InsertDatabase method of the Range. But you will need to be able to access your database using a method Word can work with (e.g. OLE DB or ODBC), so you will probably need a .odc file (or DSN) to make it work, which typically makes distribution of a solution harder. It may also be difficult to prevent security information from being stored in the .docx or .odc/DSN.
THere is an article here that provides some code for method (1) and more information about applying formatting.

Validate a csv file

This is my sample file
#%cty_id1,#%ccy_id2,#%cty_src,#%cty_cd3,#%cty_nm4,#%cty_reg5,#%cty_natnl6,#%cty_bus7,#%cty_data8
690,ALL2,,AL,ALBALODMNIA,,,,
90,ALL2,,,AQ,AKNTARLDKCTICA,,,
161,IDR2,,AZ,AZLKFMERBALFKIJAN,,,,
252,LTL2,,BJ,BENLFMIN,,,,
206,CVE2,,BL,SAILFKNT BAFSDRTHLEMY,,,,
360,,,BW2,BOPSLFTSWLSOANA,,,,
The problem is for #%cty_cd3 is a standard column(NOT NULL) with length 2 letters only, but in sql server the record shifts to the other column,(due to a extra comma in btw)how do i validate a csv file,to make sure that
when there's a 2 character word need to be only in 4 column?
there are around 10000 records ?
Set of rules Defined !
Should have a standard set of delimiters for eachrow
if not
Check for NOT NULL values having Null values
If found Null
remove delimiter at the pointer
The 3 ,,, are not replaced with 2 ,,
#UPDATED : Can i know if this can be done using a script ?
Updated i need only a function That operates on records like
90,ALL2,,,AQ,AKNTARLDKCTICA,,, correct them using a Regex or any other method and put back into the source file !
Your best bet here may be to use the tSchemaComplianceCheck component in Talend.
If you read the file in with a tFileInputDelimited component and then check it with the tSchemaComplianceCheck where you set cty_cd to not nullable then it will reject your Antarctica row simply for the null where you expect no nulls.
From here you can use a tMap and simply map the fields to the one above.
You should be able to easily tweak this as necessary, potentially with further tSchemaComplianceChecks down the reject lines and mapping to suit. This method is a lot more self explanatory and you don't have to deal with complicated regex's that need complicated management when you want to accommodate different variations of your file structure with the benefit that you will always capture all of the well formatted rows.
You could try to delete the empty field in column 4, if column no. 4 is not a two-character field, as follows:
awk 'BEGIN {FS=OFS=","}
{
for (i=1; i<=NF; i++) {
if (!(i==4 && length($4)!=4))
printf "%s%s",$i,(i<NF)?OFS:ORS
}
}' file.csv
Output:
"id","cty_ccy_id","cty_src","cty_nm","cty_region","cty_natnl","cty_bus_load","cty_data_load"
6,"ALL",,"AL","ALBANIA",,,,
9,"ALL",,"AQ","ANTARCTICA",,,
16,"IDR",,"AZ","AZERBAIJAN",,,,
25,"LTL",,"BJ","BENIN",,,,
26,"CVE",,"BL","SAINT BARTH�LEMY",,,,
36,,,"BW","BOTSWANA",,,,
41,"BNS",,"CF","CENTRAL AFRICAN REPUBLIC",,,,
47,"CVE",,"CL","CHILE",,,,
50,"IDR",,"CO","COLOMBIA",,,,
61,"BNS",,"DK","DENMARK",,,,
Note:
We use length($4)!=4 since we assume two characters in column 4, but we also have to add two extra characters for the double quotes..
The solution is to use a look-ahead regex, as suggested before. To reproduce your issue I used this:
"\\,\\,\\,(?=\\\"[A-Z]{2}\\\")"
which matches three commas followed by two quoted uppercase letters, but not including these in the match. Ofc you could need to adjust it a bit for your needs (ie. an arbitrary numbers of commas rather than exactly three).
But you cannot use it in Talend directly without tons of errors. Here's how to design your job:
In other words, you need to read the file line by line, no fields yet. Then, inside the tMap, do the match&replace, like:
row1.line.replaceAll("\\,\\,\\,(?=\\\"[A-Z]{2}\\\")", ",,")
and finally tokenize the line using "," as separator to get your final schema. You probably need to manually trim out the quotes here and there, since tExtractDelimitedFields won't.
Here's an output example (needs some cleaning, ofc):
You don't need to entry the schema for tExtractDelimitedFields by hand. Use the wizard to record a DelimitedFile Schema into the metadata repository, as you probably already did. You can use this schema as a Generic Schema, too, fitting it to the outgoing connection of tExtractDelimitedField. Not something the purists hang around, but it works and saves time.
About your UI problems, they are often related to file encodings and locale settings. Don't worry too much, they (usually) won't affect the job execution.
EDIT: here's a sample TOS job which shows the solution, just import in your project: TOS job archive
EDIT2: added some screenshots
Coming to the party late with a VBA based approach. An alternative way to regex is to to parse the file and remove a comma when the 4th field is empty. Using microsoft scripting runtime this can be acheived the code opens a the file then reads each line, copying it to a new temporary file. If the 4 element is empty, if it is it writes a line with the extra comma removed. The cleaned data is then copied to the origonal file and the temporary file is deleted. It seems a bit of a long way round, but it when I tested it on a file of 14000 rows based on your sample it took under 2 seconds to complete.
Sub Remove4thFieldIfEmpty()
Const iNUMBER_OF_FIELDS As Integer = 9
Dim str As String
Dim fileHandleInput As Scripting.TextStream
Dim fileHandleCleaned As Scripting.TextStream
Dim fsoObject As Scripting.FileSystemObject
Dim sPath As String
Dim sFilenameCleaned As String
Dim sFilenameInput As String
Dim vFields As Variant
Dim iCounter As Integer
Dim sNewString As String
sFilenameInput = "Regex.CSV"
sFilenameCleaned = "Cleaned.CSV"
Set fsoObject = New FileSystemObject
sPath = ThisWorkbook.Path & "\"
Set fileHandleInput = fsoObject.OpenTextFile(sPath & sFilenameInput)
If fsoObject.FileExists(sPath & sFilenameCleaned) Then
Set fileHandleCleaned = fsoObject.OpenTextFile(sPath & sFilenameCleaned, ForWriting)
Else
Set fileHandleCleaned = fsoObject.CreateTextFile((sPath & sFilenameCleaned), True)
End If
Do While Not fileHandleInput.AtEndOfStream
str = fileHandleInput.ReadLine
vFields = Split(str, ",")
If vFields(3) = "" Then
sNewString = vFields(0)
For iCounter = 1 To UBound(vFields)
If iCounter <> 3 Then sNewString = sNewString & "," & vFields(iCounter)
Next iCounter
str = sNewString
End If
fileHandleCleaned.WriteLine (str)
Loop
fileHandleInput.Close
fileHandleCleaned.Close
Set fileHandleInput = fsoObject.OpenTextFile(sPath & sFilenameInput, ForWriting)
Set fileHandleCleaned = fsoObject.OpenTextFile(sPath & sFilenameCleaned)
Do While Not fileHandleCleaned.AtEndOfStream
fileHandleInput.WriteLine (fileHandleCleaned.ReadLine)
Loop
fileHandleInput.Close
fileHandleCleaned.Close
Set fileHandleCleaned = Nothing
Set fileHandleInput = Nothing
KillFile (sPath & sFilenameCleaned)
Set fsoObject = Nothing
End Sub
If that's the only problem (and if you never have a comma in the field bt_cty_ccy_id), then you could remove such an extra comma by loading your file into an editor that supports regexes and have it replace
^([^,]*,[^,]*,[^,]*,),(?="[A-Z]{2}")
with \1.
i would question the source system which is sending you this file as to why this extra comma in between for some rows? I guess you would be using comma as a delimeter for importing this .csv file into talend.
(or another suggestion would be to ask for semi colon as column separator in the input file)
9,"ALL",,,"AQ","ANTARCTICA",,,,
will be
9;"ALL";,;"AQ";"ANTARCTICA";;;;