How to read quoted field from CSV using VBScript - parsing

In a sample.csv file, which has fixed number of columns, I have to extract a particular field value and store it in a variable using VBScript.
sample.csv
100,SN,100.SN,"100|SN| 435623| serkasg| 15.32|
100|SN| 435624| serkasg| 15.353|
100|SN| 437825| serkasg| 15.353|"," 0 2345"
101,SN,100.SN,"100|SN| 435623| serkasg| 15.32|
100|SN| 435624| serkasg| 15.353|
100|SN| 437825| serkasg| 15.353|"," 0 2346"
I want to parse the last two fields which are within double quotes and store them in two different array variables for each row.

You could try using an ADO connection
Option Explicit
dim ado: set ado = CreateObject("ADODB.Connection")
ado.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\txtFilesFolder\;Extended Properties=""text;HDR=No;FMT=Delimited"";"
ado.open
dim recordSet: set recordSet = ado.Execute("SELECT * FROM [samples.csv]")
dim field3, field4
do until recordSet.EOF
field3 = recordSet.Fields(3).Value
field4 = recordSet.Fields(4).Value
' use your fields here
recordSet.MoveNext
loop
recordSet.close
ado.close
You may have an issue if those fields are greater than 255 characters in length - if they are, they may return truncated. You also may have better luck with ODBC or ACE connection strings instead of the Jet one I've used here.

Since CSV's are comma-separated, you can use the Split() function to separate the fields into an array:
' Read a line from the CSV...
strLine = myCSV.ReadLine()
' Split by comma into an array...
a = Split(strLine, ",")
Since you have a static number of columns (5), the last field will always be a(4) and the second-to-last field will be a(3).

Your CSV data seems to contain 2 embedded hard returns (CR, LF) per line. Then the first line ReadLine returns is:
100,SN,100.SN,"100|SN| 435623| serkasg| 15.32|
The solution below unwraps these lines before extracting the required fields.
Option Explicit
Const ForReading = 1
Const ForAppending = 8
Const TristateUseDefault = 2 ' Opens the file using the system default.
Const TristateTrue = 1 ' Opens the file as Unicode.
Const TristateFalse = 0 ' Opens the file as ASCII.
Dim FSO, TextStream, Line, LineNo, Fields, Field4, Field5
ExtractFields "sample.csv"
Sub ExtractFields(FileName)
Set FSO = CreateObject("Scripting.FileSystemObject")
If FSO.FileExists(FileName) Then
Line = ""
LineNo = 0
Set TextStream = FSO.OpenTextFile(FileName, ForReading, False, TristateFalse)
Do While Not TextStream.AtEndOfStream
Line = Line & TextStream.ReadLine()
LineNo = LineNo + 1
If LineNo mod 3 = 0 Then
Fields = Split(Line, ",")
Field4 = Fields(3)
Field5 = Fields(4)
MsgBox "Line " & LineNo / 3 & ": " & vbNewLine & vbNewLine _
& "Field4: " & Field4 & vbNewLine & vbNewLine _
& "Field5: " & Field5
Line = ""
End If
Loop
TextStream.Close()
Else
MsgBox "File " & FileName & " ... Not found"
End If
End Sub

Here is an alternative solution that allows for single or multiline CSV records. It uses a regular expression which simplifies the logic for handling multiline records. This solution does not remove CRLF characters embedded in a record; I've left that as an exercise for you :)
Option Explicit
Const ForReading = 1
Const ForAppending = 8
Const TristateUseDefault = 2 ' Opens the file using the system default.
Const TristateTrue = 1 ' Opens the file as Unicode.
Const TristateFalse = 0 ' Opens the file as ASCII.
Dim FSO, TextStream, Text, MyRegExp, MyMatches, MyMatch, Field4, Field5
ExtractFields "sample.csv"
Sub ExtractFields(FileName)
Set FSO = CreateObject("Scripting.FileSystemObject")
If FSO.FileExists(FileName) Then
Set MyRegExp = New RegExp
MyRegExp.Multiline = True
MyRegExp.Global = True
MyRegExp.Pattern = """([^""]+)"",""([^""]+)"""
Set TextStream = FSO.OpenTextFile(FileName, ForReading, False, TristateFalse)
Text = TextStream.ReadAll
Set MyMatches = MyRegExp.Execute(Text)
For Each MyMatch in MyMatches
Field4 = SubMatches(0)
Field5 = SubMatches(1)
MsgBox "Field4: " & vbNewLine & Field4 & vbNewLine & vbNewLine _
& "Field5: " & vbNewLine & Field5, 0, "Found Match"
Next
Set MyMatches = Nothing
TextStream.Close()
Else
MsgBox "File " & FileName & " ... Not found"
End If
End Sub

Related

Migration Zephyr test-cases steps from Jira server to Jira cloud

Now we are on the process of migration from Jira server to the Jira cloud.
We are using Zephyr for Test Cases.
For now we have 1843 and they must be migrated as other tickets to Jira cloud.
We do not need to migrate all Test Cycles and all history of test execution, we need only Test Cases to use it in future Test Cycles.
This article contains answer on the same question
https://support.getzephyr.com/hc/en-us/community/posts/205799785-How-to-migrate-from-JIRA-Server-to-JIRA-Cloud
but utility doesn't work properly for me, after pressing Start Import button nothing happens.
How to migrate Test Cases from server Jira to cloud with Test Steps in Zephyr?
Finally I found the solution how to import all 1843 Test Cases automatically via tool from the article mention in the question.
Our test-cases were migrated to the cloud Jira as usual tickets. They have no Test Steps but have all other information like Description, Labels and other which relate to Jira fields. Further I will show how to migrate all steps to the migrated Test Cases without steps.
Go to your Jira and Export Test Cases that you need to Excel file. It can be done from this screen
https://zephyrdocs.atlassian.net/wiki/spaces/ZTD/pages/12386325/Search+Test+Executions
Download .jar file from the
https://bitbucket.org/zfjdeveloper/zfj-importer/downloads/
In cmd run this jar file via command java -jar zfj-importer-utility-0.40.jar
I tried to run jar file by double click, application opens but after configuration and press button Start Import nothing happens.
Only after opening from cmd everything works.
Plus in cmd you can see progress and error details which will help you in debug.
Configure utility as in documentation https://bitbucket.org/zfjdeveloper/zfj-importer/wiki/Home
At this point I though that after pressing Start Import everything will be perfect, but no.
In console I found a lot of error, their reason was a lot of line breaks inside test steps.
Lets say you have one step with one row in Step field, one row In Test Data field but in Execution result field you have text with line breaks, lets say 4 rows. For this case in excel Execution result field will be 4 different columns, Step field and Test Data as one merged column.
And based on utility rules there impossible to have result without step. (such issue can be if you have line break in Step field and Test Data).
Below I will show how I handle it.
I decided to write Excel function which will get rows from not merged rows for one step, concatenate them and provide to import.
Excuse me for my VBA, I have never use it before. Everything that I wrote can be rewritten in better way and in one script, but it works for me and I do not want to spend time more on this issue, so let go.
Below you can find 4 excel function. 3 of them are quite similar and difference is only in one letter. Last scrip is for deleting empty rows which were concatenated, without it steps with value "null" will be created.
Public Const lastTableRow = 3872
Function ConvertSteps()
Dim callerRow As Long
Dim isValueInStepId As Boolean
Dim isNoValueInNextStepId As Boolean
Dim result As String
Dim baseColumnLetter As String
Dim stepIdColumnLetter As String
callerRow = Application.Caller.row
baseColumnLetter = "S"
stepIdColumnLetter = "Q"
Debug.Print "processed row is: " & callerRow
isValueInStepId = (Range(stepIdColumnLetter & callerRow).Value <> "")
isNoValueInNextStepId = (Range(stepIdColumnLetter & (callerRow + 1)).Value = "")
If isValueInStepId And isNoValueInNextStepId Then
Dim i As Integer
i = 1
result = Range(baseColumnLetter & callerRow).Value
Do While Range(stepIdColumnLetter & (callerRow + i)).Value = "" And (callerRow + i) <= lastTableRow
result = result & " " & Range(baseColumnLetter & (callerRow + i)).Value
i = i + 1
Loop
ConvertSteps = result
Else
If Range(baseColumnLetter & (callerRow)).Value = "" Then
ConvertSteps = ""
Else
ConvertSteps = Range(baseColumnLetter & (callerRow)).Value
End If
End If
End Function
Function ConvertTestData()
Dim callerRow As Long
Dim isValueInStepId As Boolean
Dim isNoValueInNextStepId As Boolean
Dim result As String
Dim baseColumnLetter As String
Dim stepIdColumnLetter As String
callerRow = Application.Caller.row
baseColumnLetter = "T"
stepIdColumnLetter = "Q"
Debug.Print "processed row is: " & callerRow
isValueInStepId = (Range(stepIdColumnLetter & callerRow).Value <> "")
isNoValueInNextStepId = (Range(stepIdColumnLetter & (callerRow + 1)).Value = "")
If isValueInStepId And isNoValueInNextStepId Then
Dim i As Integer
i = 1
result = Range(baseColumnLetter & callerRow).Value
Do While Range(stepIdColumnLetter & (callerRow + i)).Value = "" And (callerRow + i) <= lastTableRow
result = result & " " & Range(baseColumnLetter & (callerRow + i)).Value
i = i + 1
Loop
ConvertTestData = result
Else
If Range(baseColumnLetter & (callerRow)).Value = "" Then
ConvertTestData = ""
Else
ConvertTestData = Range(baseColumnLetter & (callerRow)).Value
End If
End If
End Function
Function ConvertResult()
Dim callerRow As Long
Dim isValueInStepId As Boolean
Dim isNoValueInNextStepId As Boolean
Dim result As String
Dim baseColumnLetter As String
Dim stepIdColumnLetter As String
callerRow = Application.Caller.row
baseColumnLetter = "U"
stepIdColumnLetter = "Q"
Debug.Print "processed row is: " & callerRow
isValueInStepId = (Range(stepIdColumnLetter & callerRow).Value <> "")
isNoValueInNextStepId = (Range(stepIdColumnLetter & (callerRow + 1)).Value = "")
If isValueInStepId And isNoValueInNextStepId Then
Dim i As Integer
i = 1
result = Range(baseColumnLetter & callerRow).Value
Do While Range(stepIdColumnLetter & (callerRow + i)).Value = "" And (callerRow + i) <= lastTableRow
result = result & " " & Range(baseColumnLetter & (callerRow + i)).Value
i = i + 1
Loop
ConvertResult = result
Else
If Range(baseColumnLetter & (callerRow)).Value = "" Then
ConvertResult = ""
Else
ConvertResult = Range(baseColumnLetter & (callerRow)).Value
End If
End If
End Function
Public Sub DeleteBlankRows()
Dim SourceRange As Range
Dim EntireRow As Range
Set SourceRange = Range("Q1", "Q" & lastTableRow)
If Not (SourceRange Is Nothing) Then
Application.ScreenUpdating = False
For i = SourceRange.Rows.Count To 1 Step -1
Set EntireRow = SourceRange.Cells(i, 1).EntireRow
Debug.Print SourceRange.Cells(i, 1).Value
If SourceRange.Cells(i, 1).Value = 0 Then
EntireRow.Delete
End If
Next
Application.ScreenUpdating = True
End If
End Sub
Let's open Excel file and save it in .xlsm format to apply custom functions.
Import functions to Excel
in the top of the code set in variable lastTableRow last row with Test Case step in your Excel.
Now we need 3 new columns to save transferred Step, Test Data and Result fields. For this purpose we can use last column Comments, copy and past it two times. Now we have 3 empty column W, X, Y for our purpose.
For all rows in column W apply formula =ConvertSteps() to agregate steps (it can take some time)
For all rows in column X apply formula =ConvertTestData() to agregate test data (it can take some time)
For all rows in column Y apply formula =ConvertResult() to agregate results (it can take some time)
Now we have to convert values in new columns from formula to their string value. To do it select all table and press Ctrl+C. Then press right button and choose past values.
Run DeleteBlankRows macros to delete all rows that we do not need to import.
Save file in .xml format.
Choose this file in Utility and press Start Import
In cmd you can see a few errors. In my case they were releted to situation when there is no step description but there is expected result. If they are quite seldom as in my case, it's easier to change it mannualy in Execel file. If there a lot of them you can handle this case in custom function.
So thats it, this solution helped me to import 1800+ Test Cases.
I have exported them partially, by 500 and for me it takes about 3 hour to import all Test Cases.

I am trying to link a subform using Combo box, i write the following code

Dim mydrawing As String
mydrawing = "select * from tbl_welddata where([Drawingno] = " & Me!CboDrawingno & ")"
Me.tbl_welddatasubform.Form.RecordSource = mydrawing Me.tbl_welddatasubform.Form.Requery
I am getting an error message as;
Data type mismatch error 3464
Every time when I debug it pointing to this line
Me.tbl_welddatasubform.Form.RecordSource = mydrawing
could some one help me to sort it.
Put in a debug line, and study the output:
Dim mydrawing As String
mydrawing = "select * from tbl_welddata where([Drawingno] = " & Me!CboDrawingno & ")"
' If text, quotes are needed:
' mydrawing = "select * from tbl_welddata where([Drawingno] = '" & Me!CboDrawingno & "')"
Debug.Print mydrawing
Me!tbl_welddatasubform.Form.RecordSource = mydrawing
A requery is not needed.
Can you apply the resulting mydrawing manually?
Is Drawingno a number, not text?
Is tbl_welddatasubform the name of the subform control?

VBA to read in tab delimited file

I have some code which reads in a tab delimited file where cell reference B2 matches the reference in the first column in the tab delimited file. This works fine where the text file is small. The below works on a sample file with aa bb and cc as the headers with dummy data.
Option Explicit
Sub TestImport()
Call ImportTextFile(Sheet1.Range("B1"), vbTab, Sheet2.Range("A4"))
End Sub
Public Sub ImportTextFile(strFileName As String, strSeparator As String, rngTgt As Range)
Dim lngTgtRow As Long
Dim lngTgtCol As Long
Dim varTemp As Variant
Dim strWholeLine As String
Dim intPos As Integer
Dim intNextPos As Integer
Dim intTgtColIndex As Integer
Dim wks As Worksheet
Set wks = rngTgt.Parent
intTgtColIndex = rngTgt.Column
lngTgtRow = rngTgt.Row
Open strFileName For Input Access Read As #1
While Not EOF(1)
Line Input #1, strWholeLine
varTemp = Split(strWholeLine, strSeparator)
If CStr(varTemp(0)) = CStr(Range("B2")) Then
wks.Cells(lngTgtRow, intTgtColIndex).Resize(, UBound(varTemp) + 1).Value = varTemp
lngTgtRow = lngTgtRow + 1
End If
Wend
Close #1
Set wks = Nothing
End Sub
I am trying to get the below bit of code to work using ADO as this will run much faster on a text file with a couple of million records however I am getting an error on the '.Open str' part of the code (no value given for one or more required parameters).
It looks like it is to do with how I am defining the string- could you review and see if there is something I am missing...?
Sub QueryTextFile()
t = Timer
Dim cnn As Object
Dim str As String
Set cnn = CreateObject("ADODB.Connection")
cnn.Provider = "Microsoft.Jet.OLEDB.4.0"
cnn.ConnectionString = "Data Source=C:\Users\Davids Laptop\Documents\Other Ad Hoc\Test Files\;Extended Properties=""text;HDR=Yes;FMT=Delimited;"""
cnn.Open
Dim rs As Object
Set rs = CreateObject("ADODB.Recordset")
str = "SELECT * FROM [test1.txt] WHERE [aa]=" & Chr(34) & Range("B2") & Chr(34)
With rs
.ActiveConnection = cnn
.Open str
Sheet1.Range("A4").CopyFromRecordset rs
.Close
End With
cnn.Close
MsgBox Timer - t
End Sub

Parsing POST request with unexpected URL encoding

This question follows an earlier one.
Here is some code that reproduces the problem:
POST:
str = "accountRequest=<NewUser>" & vbLf & _
"Hello" & vbTab & "World" & vbLf & _
"</NewUser>"
Set objHTTP = Server.CreateObject("Msxml2.ServerXMLHTTP.3.0")
objHTTP.open "POST", "service.asp", False
objHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
objHTTP.send str
response.Write(objHTTP.responseText)
Set objHTTP = Nothing
service.asp:
function w (str)
response.Write(str & "<br>")
end function
str = request.Form("accountRequest")
w(str)
w("Tabs: "& InStr(str,vbTab))
w("Lines: "& InStr(str,vbLf))
output:
HelloWorld
Tabs: 0
Lines: 0
Can anyone please help?
Try:
Replace(Request.Form("accountRequest"), vbLF, vbCRLF))
Or:
Replace(Request.Form("accountRequest"), vbLF, "<br>"))|
Depending on where you're displaying it, either should work.
Or possibly this:
Function URLDecode(sConvert)
Dim aSplit
Dim sOutput
Dim I
If IsNull(sConvert) Then
URLDecode = ""
Exit Function
End If
' convert all pluses to spaces
sOutput = REPLACE(sConvert, "+", " ")
' next convert %hexdigits to the character
aSplit = Split(sOutput, "%")
If IsArray(aSplit) Then
sOutput = aSplit(0)
For I = 0 to UBound(aSplit) - 1
sOutput = sOutput & _
Chr("&H" & Left(aSplit(i + 1), 2)) &_
Right(aSplit(i + 1), Len(aSplit(i + 1)) - 2)
Next
End If
URLDecode = sOutput
End Function
From here: http://www.aspnut.com/reference/encoding.asp
If they are coming across the wire as the actual "\" and "n" characters, you can do a replace on those characters with the appropriate vbCRLF and vbTAB constants.
Finally figured out that ASP Request.Form method doesn't preserve tabs if they're in the "\t" format (as opposed to URL encoded). However, PHP's $_POST does.

Open Office Spreadsheet (Calc) - Concatenate text cells with delimiters

I am using Open Office's spreadsheet program and am trying to concatenate several text cells together with delimeters. For example, suppose I have the cells below:
+--------+
| cell 1 |
+--------+
| cell 2 |
+--------+
| cell 3 |
+--------+
| cell 4 |
+--------+
| cell 5 |
+--------+
I would like to concatenate them with delimiters so that the result is in one cell like this one:
+----------------------------------------------+
| (cell 1),(cell 2),(cell 3),(cell 4),(cell 5) |
+----------------------------------------------+
My first thought was to try and make a macro or something, but I don't think open office supports those. Any ideas?
Thanks a lot Markus for finding a solution to this.
Here are some slightly more detailed instructions for the benefit of OpenOffice Basic newbies like myself. This applies to version 3.1:
Tools -> Macros -> Organize Macros -> OpenOffice.org Basic...
Now select from the explorer tree where you want your function live,
e.g. it can be in your own macro library (My Macros / Standard) or
stored directly in the current spreadsheet.
Now enter a new Macro name and click New to open the OO.org Basic IDE. You'll see a REM
statement and some stub Sub definitions. Delete all that and replace
it with:
Function STRJOIN(range, Optional delimiter As String, Optional before As String, Optional after As String)
Dim row, col As Integer
Dim result, cell As String
result = ""
If IsMissing(delimiter) Then
delimiter = ","
End If
If IsMissing(before) Then
before = ""
End If
If IsMissing(after) Then
after = ""
End If
If NOT IsMissing(range) Then
If NOT IsArray(range) Then
result = before & range & after
Else
For row = LBound(range, 1) To UBound(range, 1)
For col = LBound(range, 2) To UBound(range, 2)
cell = range(row, col)
If cell <> 0 AND Len(Trim(cell)) <> 0 Then
If result <> "" Then
result = result & delimiter
End If
result = result & before & range(row, col) & after
End If
Next
Next
End If
End If
STRJOIN = result
End Function
The above code has some slight improvements from Markus' original:
Doesn't start with a delimiter when the first cell in the range is empty.
Allows optional choice of the delimiter (defaults to ","), and the
strings which go before and after each non-blank entry in the range
(default to "").
I renamed it STRJOIN since "join" is the typical name of this
function in several popular languages, such as Perl, Python, and Ruby.
Variables all lowercase
Now save the macro, go to the cell where you want the join to appear,
and type:
=STRJOIN(C3:C50)
replacing C3:C50 with the range of strings you want to join.
To customise the delimiter, instead use something like:
=STRJOIN(C3:C50; " / ")
If you wanted to join a bunch of email addresses, you could use:
=STRJOIN(C3:C50; ", "; "<"; ">")
and the result would be something like
<foo#bar.com>, <baz#qux.org>, <another#email.address>, <and#so.on>
Well, after a lot more searching and experimenting, I found you can make your own functions in calc. This is a function I made that does what I want:
Function STRCONCAT(range)
Dim Row, Col As Integer
Dim Result As String
Dim Temp As String
Result = ""
Temp = ""
If NOT IsMissing(range) Then
If NOT IsArray(range) Then
Result = "(" & range & ")"
Else
For Row = LBound(range, 1) To UBound(range, 1)
For Col = LBound(range, 2) To UBound(range, 2)
Temp = range(Row, Col)
Temp = Trim(Temp)
If range(Row, Col) <> 0 AND Len(Temp) <> 0 Then
If(NOT (Row = 1 AND Col = 1)) Then Result = Result & ", "
Result = Result & "(" & range(Row, Col) & ") "
End If
Next
Next
End If
End If
STRCONCAT = Result
End Function
Ever so often I'd enjoy the ease and quickness of replace & calculation Options as well as in general the quick handling & modifying Options, when once again sitting in front of a dumped-file-lists or whatsoever.
I never understood why they didn't include such an essential function right from the start, really.
It's based on Adam's script, but with the extension to swap CONCAT from horizontal to vertical, while still keeping the delimiters in order.
Function CONCAT2D(Optional range, Optional delx As String, Optional dely As String, _
Optional xcell As String, Optional cellx As String, _
Optional swop As Integer)
Dim xy(1), xyi(1), s(1) As Integer
Dim out, cell, del, dxy(1) As String
'ReDim range(2, 1) 'Gen.RandomMatrix 4 Debugging
'For i = LBound(range, 1) To UBound(range, 1)
' For j = LBound(range, 2) To UBound(range, 2)
' Randomize
' range(i,j) = Int((100 * Rnd) )
' Next
'Next
out = ""
If IsMissing(delx) Then : delx = "," : End If
If IsMissing(dely) Then : dely = delx() : End If
If IsMissing(xcell) Then : xcell = "" : End If
If IsMissing(cellx) Then : cellx = xcell() : End If
If IsMissing(swop) Then : swop = 0 : End If
dxy(0) = delx() : dxy(1) = dely()
xyi(0) = 1 : xyi(1) = 2
If swop = 0 Then : s(0) = 0 : s(1) = 1
Else s(0) = 1 : s(1) = 0 : End If
If NOT IsMissing(range) Then
If NOT IsArray(range) _
Then : out = xcell & range & cellx
Else del = delx
For xy(s(0)) = LBound(range, xyi(s(0))) To UBound(range, xyi(s(0))
For xy(s(1)) = LBound(range, xyi(s(1))) To UBound(range, xyi(s(1))
cell = range(xy(0), xy(1))
If cell <> 0 AND Len(Trim(cell)) <> 0 _
Then : If out <> "" Then : out = out & del : End If
out = out & xcell & cell & cellx
del = dxy(s(0))
End If
Next : del = dxy(s(1))
Next
End If
Else out = "ERR"
End If
CONCAT2D = out
End Function

Resources