loading Data in VBA from a text file - file

I am not very familiar with VBA but need to use it for a new software program I am using (not Microsoft related)
I have a text file that has columns of data I would like to read into VBA.
Specifically the text file has 4 entries per row. Thus I would like to load in the column vectors (N by 1).
The text file is separated by a space between each entry.
So for example I want to load in column one and save it as array A, then column two and save as array B, then column three and save as array C, and then column four and save as array D.
This code snippet found below from http://www.tek-tips.com/faqs.cfm?fid=482 is something I found that can load in text to an array, but I need to adapt it to be able to save the columns as different arrays as specified above...
Open "MyFile.txt" For Input As #1
ReDim Txt$(0)
Do While Not EOF(1)
ReDim Preserve Txt$(UBound(Txt$) + 1)
Input #1, Txt$(UBound(Txt$))
Loop
Close #1

For this example, you will need a file called schema.ini in the same directory as the text file. It should contain:
[Import.txt]
Format=Delimited( )
Where Import.txt is the name of the file (http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx).
You can then use this, which should work in VBScript or VBA with very little tampering:
Set cn = CreateObject("ADODB.Connection")
'Note HDR=Yes, that is, first row contains field names '
'and FMT delimted, ie CSV '
strCon="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Docs\;" _
& "Extended Properties=""text;HDR=Yes;FMT=Delimited"";"
cn.Open strcon
strSQL="SELECT * FROM Import.txt" _
set rs = createobject("adodb.recordset")
rs.open strSQL,cn
MsgBox rs(2)
MsgBox rs.GetString
The first message box should return the third column of the first row, it is a test that it works.
The second message box should return the whole file, so don't use it with a large set. The recordset can be manipulated, or you can use .GetRows to create an array of values (http://www.w3schools.com/ado/met_rs_getrows.asp)

Seems the remaining problem is to convert from an array of lines to four arrays of columns.
Maybe this snippet helps
Option Explicit
Option Base 0
Sub import()
Dim sTxt() As String
Dim sLine As Variant
Dim iCountLines As Long
Dim iRowIterator As Long
Dim i As Long
Dim sRow() As String
Dim sColumnA() As String
Dim sColumnB() As String
Dim sColumnC() As String
Dim sColumnD() As String
' read in file '
Open "MyFile.txt" For Input As #1
ReDim sTxt(0)
Do While Not EOF(1)
Input #1, sTxt(UBound(sTxt))
ReDim Preserve sTxt(UBound(sTxt) + 1)
Loop
Close #1
' dim array for each columns '
iCountLines = UBound(sTxt)
Debug.Print "working with ", iCountLines, "lines"
ReDim sColumnA(iCountLines)
ReDim sColumnB(iCountLines)
ReDim sColumnC(iCountLines)
ReDim sColumnD(iCountLines)
' "transpose" sTxt '
iRowIterator = 0
For Each sLine In sTxt
sRow = Split(sLine, " ")
If UBound(sRow) = 3 Then
sColumnA(iRowIterator) = sRow(0)
sColumnB(iRowIterator) = sRow(1)
sColumnC(iRowIterator) = sRow(2)
sColumnD(iRowIterator) = sRow(3)
iRowIterator = iRowIterator + 1
End If
Next sLine
' now work with sColumnX '
Debug.Print "Column A"
For i = 0 To iCountLines
Debug.Print sColumnA(i)
Next i
End Sub

There is few detais in your question, but i would suggest using "Text to column"
If you're not very familiar with VBA programming try recording macro with this steps:
Import file to Excel
select column A
select "Text to columns" form tools menu
choose delimited by space
This way you'll get array of data you asked for, now assigning to any variables you want shouldn't be a problem.
EDIT (Without using Excel):
Take a look on that FSO method.
by replacing
MsgBox strLing
with some kind of split function, like
strTemp = Split(strLine, " ")
You'll be able to loop through all the values in your source file, would that work?

Related

Array loses value when a vba opened workbook it was read from closes

I want to use some data stored in a VBA defined array which I read in from another workbook. I process the data in it's original workbook to remove all spaces, read the data in, and close the original workbook without saving. Then when I call another sub I find the array, and range variables I extracted from that workbook are no longer defined. Is it possible to make the values stick without keeping the workbook open? The problem is near the end of the routine, and noted in caps.
THANKS!
All subroutines that are called work properly except the last one, which is still being defined.
I will share the other routines if needed or someone thinks it will solve a problem for them --DONE. There is no error checking in any of them as of yet!
Sub UpdateEmployeewithholding()
'This sub will clean employee withholding as it is exported from QuickBooks and then read the file into this workbook
'The path is already stored in the names manager
'This routine needs to integrate the existing subs "changevalueofname" and "getpath". They should update before executing the balance of this routine
Dim MyWorkBook As Workbook
Dim MyPath As Variant 'Contains path to employee withholding spreadsheet as exported from quickbooks. This sheet is to be modified for reading, but not saved
Dim MyRange As Range 'Contains a defined range after setting it so
Dim whichrow As Variant 'Marks the starting point for routines that find and delete blanks as well as those that define range values and scan them into an array
Dim Direction As Variant 'Defines whether we are progressing over "Rows" or "Columns"
Dim ArrayWidth As Range 'Holds the top row addresses of the array
Dim ArrayHeight As Range 'Holds the left column addresses of the array
Dim MyArray As Variant 'Holds the array to transfer to this spreadsheet
whichrow = 1 'We are starting in cell A1 or R1C1
Direction = "Rows"
'******************************************************************************************************
'***INSERT Code that will read the string value stored in the name manager Name "PathToEmployeeWithholding" into the variable "MyPath"
' and eliminate the hard coded path from the routine
' STILL MISSING
'*****************************************************************************************************
'Setting MyPath to the fixed path to employee withholding until we can get the routine to open the workbook from a varialbe
'stored in the name manager
MyPath = "D:\redacted\Employee Withholding .xlsx"
'ActiveWorkbook.Names (PathToEmployeeWithholding)
Debug.Print MyPath
Set MyWorkBook = Workbooks.Open(MyPath)
Debug.Print ActiveWorkbook.Name
With MyWorkBook
.Activate
Call FindDataRange(MyRange, whichrow, Direction)
Debug.Print MyRange.Address
Call DeleteBlanks(MyRange, Direction)
'Use ArrayWidth and ArrayHeight with the routine FindDataRange
'to get the width and height of the final arrray that will be read into the spreadsheet
'close without saving the data, so it will be preserved as it came from quickbooks
Call FindDataRange(ArrayWidth, whichrow, Direction)
Direction = "Columns)"
Call FindDataRange(ArrayHeight, whichrow, Direction)
Debug.Print "Array Width " & ArrayWidth.Address
Debug.Print "array height " & ArrayHeight.Address
'Insert a call to a routine that will copy an array consisting of myrange as the top plus all the rows under it, which include employee info
Call ReadArray(MyArray, ArrayWidth, ArrayHeight)
'Insert code to test employee sheets and recap sheet, as well as sheet containing lookup data.
'As that code determines what the current structure is, it should update the structure to conform to the imported array
'If no data exists in the spreadsheet, then we create pages for each new employee, and write the Recap and the Lookup Table
'If data already exists in the spreadsheet, then we maintain the existing employees. and append their sheets,
'and add new Data to the lookup table, (Questionable whether we should totally rewrite the lookup table or just append
'the new data and sort by employee name to maintain old employee data)
'and rewrite the Recap so the user only has to enter time for current employees
'NOTE the employee sheets will be labeled by their name, just as listed in the lookup table
.Close (False)
End With
ResetMessage = MsgBox("You are about to reset the spreadsheet to match the data that was just loaded. Continue?", vbOKCancel)
If ResetMessage = 2 Then
Exit Sub
End If
Call ResetWorksheets(MyArray, ArrayWidth, ArrayHeight) '**ALL OF THESE VARIBALES LOSE VALUE WHEN MY WORKBOOK CLOSES**
End Sub
Sub FindDataRange(MyRange As Range, whichrow As Variant, Direction As Variant)
'This routine will return a single row or Column range of data
'that includes the first cell in whichrow to the last cell with data in whichrow
Dim StartRange As Range
Dim FullRange As Range
If Direction = "Rows" Then
'Startrange will be first cell in whichrow
Set StartRange = Cells(whichrow, 1)
'Fullrange will be the entire row of whichrow
Set FullRange = Range(StartRange, StartRange.Offset(0, Columns.Count - 1)) 'this produced the entire whichrow row as the range.
Set MyRange = Range(StartRange, FullRange.Find("*", StartRange, xlValues, xlPart, xlByRows, xlPrevious, True)) 'startrange,xlvalues,xlpart,xlbyrows,xlPrevious,true)
'this should return the range which has the data
Debug.Print MyRange.Address
Else
'Startrange will be first cell in whichrow
Set StartRange = Cells(1, whichrow)
'Fullrange will be the entire column of whichrow
Set FullRange = Range(StartRange, StartRange.Offset(Rows.Count - 1, 0)) 'this produced the entire whichrow column as the range.
Set MyRange = Range(StartRange, FullRange.Find("*", StartRange, xlValues, xlPart, xlByColumns, xlPrevious, True)) 'startrange,xlvalues,xlpart,xlbyrows,xlPrevious,true)
'this should return the range which has the data
Debug.Print MyRange.Address
End If
End Sub
Sub DeleteBlanks(WorkingRange As Range, Direction As Variant)
'This will delete the entire row/column of blanks according to the cell in a single row/column contents
'To use it we need to input a working range that is to be considered to delete blanks from, and a direction which is either "Rows" or "Columns"
Dim Message As String
For i = WorkingRange.Cells.Count To 1 Step -1
Debug.Print Direction
Select Case Direction
Case Is = "Rows"
If WorkingRange.Cells(i) = "" Then
Debug.Print WorkingRange.Cells(i).Address
WorkingRange.Cells(i).EntireColumn.Delete
End If
Case Is = "Columns"
If WorkingRange.Cells(i) = "" Then
WorkingRange.Cells(i).EntireRow.Delete
Debug.Print WorkingRange.Cells(i).Address
End If
Case Else
Message = "You must declare a direction either Rows or Columns to search before calling this routine"
MsgBox Message, vbOKOnly, "Routine Requires a Direction"
End Select
Next
End Sub
Sub ReadArray(MyArray As Variant, ArrayWidth As Range, ArrayHeight As Range)
'This routine should read an array with a width contained in the ArrayWidth range, and a height
'contained in the ArrayHeight range. We retreive the actual size to read by using range.cells.count
Dim WidthStep As Long 'Contains the width of the array
Dim HeightStep As Long 'Contains the height of the array
Dim i As Long 'step counter for height becaue it has to be the outside loop to read in rows
Dim j As Long 'step counter for width because it has to be the inside loop to read in rows
WidthStep = ArrayWidth.Cells.Count
HeightStep = ArrayHeight.Cells.Count
ReDim MyArray(HeightStep, WidthStep)
' Let's read the array in in rows, but remember the employee names are in the left column
For i = 1 To HeightStep
For j = 1 To WidthStep
MyArray(i, j) = ArrayWidth.Cells(i, j).Value
Debug.Print MyArray(i, j)
Next j
'!!!!!!!!This routine READS LEFT TO RIGHT FIRST AND THEN TOP TO BOTTOM
'Writing must consider how it is reading to get things in the correct place
Next i
End Sub
Sub ResetWorksheets(MyArray As Variant, ArrayWidth As Range, ArrayHeight As Range)
'Currently a blank subroutine with a test to verify data transfered
For i = 1 To ArrayHeight.Cells.Count
Debug.Print MyArray(1, i).Value
Next i
End Sub
It begs further testing, but I think I figured out the problem. The data is still in the array after closing, but I have a reference to two ranges in the closed spreadsheet, which loose their cells.count value when the spreadsheet closes. If it tests out, then transferring the width and height to long variables should preserve the data.
I also had a problem with the reset worksheet subroutine, which was trying to call a .value from the array I was stepping through. (MyArray(i,j).value which was throwing an error as well.
Verified that solved the problem.
Now on to get it to open the file it reads from using a name programmatically stored in the name manager. I have code in there and blocked off that did not work, which was temporarily replaced with a static statement to get the file open so I could continue.
Thanks!
'lines so marked below were Added
Dim Width As Long 'Added Holds the array width to prevent loosing it when the original spreadsheet closes
Dim Height As Long 'Added Holds the array height to prevent loosing it when the original spreadsheet closes
Call FindDataRange(ArrayWidth, whichrow, Direction)
Width = ArrayWidth.Cells.Count 'Added
Direction = "Columns)"
Call FindDataRange(ArrayHeight, whichrow, Direction)
Debug.Print "Array Width " & ArrayWidth.Address
Debug.Print "array height " & ArrayHeight.Address
Height = ArrayHeight.Cells.Count 'Added
'Lines below were modifid to incorperate the two added variables
Sub ResetWorksheets(MyArray As Variant, Width As Long, Height As Long) 'modified
Dim CurrentWorkBook As Workbook
''Set CurrentWorkBook = ActiveWorkbook 'Save the current workbook which is the one exported from Quick Books
''ThisWorkbook.Activate 'This workbook is the one the code is in. It is also the one we need to update or create pages for
'We need to first test and see what exists in the workbook according to ranges
For j = 1 To Width 'modified
For i = 1 To Height 'modified
Debug.Print MyArray(i, j)
Next i
Next j
'CurrentWorkBook.Activate 'Restore the CurrentWorkBook to active status before returning and closing the book This should be the very 'last operation in the routine
End Sub

Subscript Out Of range when selecting an array I've created

I'm trying to create a single PDF file containing a sheet for each tab which I have listed from cell J2 in my Control sheet but I keep getting a Subscript Out Of Range error.
When I record the action I see that it creates an array of sheet names which it then selects to export, so I have a For loop which goes through the list and creates an array which adds to itself until it reaches the end of the list - the aim being to create one long string which I then select as an array.
All appears to be good (the variable PDFArray displays a string of the tab names in what appears to be the correct format) but when I get to the line 'Worksheets(Array(PDFarray)).Select' then I get the error. I've made sure the sheet names contain no undesirable characters or spaces but still no joy. Any help would be very much appreciated. Thank you
Sub B_PDFs()
Dim PDFarray As String, PDFName as String, sht As String
Sheets("Control").Select
PLFile = ActiveWorkbook.Name
PDFLoc = Application.ActiveWorkbook.Path & "\"
PDFName = Range("A20")
PDFSheetCount = Range("J1").Offset(Rows.Count - 1, 0).End(xlUp).Row
'Loop through column J and create a string with each tab name to be exported
For x = 2 To PDFSheetCount Step 1
If x = PDFSheetCount Then
sht = """ " & "" & Cells(x, 10) & """ "
Else
sht = """" & "" & Cells(x, 10) & """" & ", "
End If
PDFarray = PDFarray & sht
Next x
'Create PDF from the array above
Worksheets(Array(PDFarray)).Select - this is where I get the error Subscript Out Of Range
Selection.ExportAsFixedFormat Type:=xlTypePDF, Filename:=PFDLoc & PDFName, Quality:= _
xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas:=False,
OpenAfterPublish:=False
Workbooks(PLFile).Activate
End Sub
I don't understand why MS makes NOT requiring variable declaration the default. Select Tools/Options/Editor and check Require Variable Declaration. This will place Option Explicit at the start of any new module. To correct this module, enter it manually at the beginning.
Doing so would have enabled you to find and correct a typo in your code.
You should also be avoiding Select, Selection and Activate. They rarely serve any purpose at all, and can cause multiple problems because they lull into avoiding explicit declarations of which workbook, worksheet, etc. you need. See How to avoid using Select in Excel VBA
However in using the ExportAsFixedFormat method to export selected worksheets, it seems Selection and ActiveSheet are required for it to work.
Array(str_variable) returns an array with a single entry that contains the entire string variable. It does not interpret the string variable so as to split it into separate elements.
So, rewriting your code somewhat (I will leave it to you to clean up the PDF document):
Option Explicit
Sub B_PDFs()
Dim PDFarray As Variant, PDFName As String, PLFile As String, PDFLoc As String
Dim wsControl As Worksheet
Dim WB As Workbook
'Consider wheter you want to use ThisWorkbook or a specific workbook
Set WB = ThisWorkbook
With WB
Set wsControl = .Worksheets("Control")
PLFile = .Name
PDFLoc = .Path & "\"
End With
With wsControl
PDFName = .Range("A20")
'create PDFarray
'This will be a 1-based 2D array starting at J1
'If you need to start at J2, alter the initial cell
PDFarray = .Range(.Cells(1, 10), .Cells(.Rows.Count, 10).End(xlUp))
End With
'convert to a 1D array
PDFarray = WorksheetFunction.Transpose(PDFarray)
'Note the use of `Select` and `ActiveSheet` when using this `ExportAsFixedFormat` method
Worksheets(PDFarray).Select
'Create PDF from the array above
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:=PDFLoc & PDFName, Quality:= _
xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas:=False, OpenAfterPublish:=False
End Sub
What #RonRosenfeld has suggested is correct about select and selection. The expression you are building is string whereas, Excel expects it to be real array.
So in principle an approach like below shall work for you which will create an array for processing and can be used as you want to utilise.
Dim shtNames As Variant
Dim pdfArray
shtNames = Range("J2:J" & Range("J1").Offset(Rows.Count - 1, 0).End(xlUp).Row).Value
pdfArray = Application.Transpose(shtNames)

Looping a String Split through Column

I have a column full of data in a format I don't like, and need them in another format. Currently they are formatted like this: "190826_095630_3E_1 (ROI 0)" and I need just the "3E" portion. I have written a string split that uses the "_" and I figure I can then just take the column of data that is produced that I want, however I can only get this to work one cell at a time while I click each one. I tried to write a for loop but I am running into trouble, most likely because I used "active.cell". Does anyone have a better way to loop this split through my column? Alternatively if you also know how to just return the third string split (3E) I would really appreciate it.
'No loop: This works for one cell
Option Explicit
Sub NameTest()
Dim txt As String
Dim i As Integer
Dim FullName As Variant
txt = ActiveCell.Value
FullName = Split(txt, "_")
For i = 0 To UBound(FullName)
Cells(1, i + 1).Value = FullName(i)
Next i
End Sub
'Attempt at a loop:
Option Explicit
Sub NameTest()
Dim txt As String
Dim i As Integer
Dim FullName As Variant
Dim x As Integer
For x = 1 To 1000
txt = ActiveCell.Value
FullName = Split(txt, "_")
For i = 0 To UBound(FullName)
Cells(1, i + 1).Value = FullName(i)
Next i
Next x
End Sub
I would like to get this to run until the last cell with data in a given column.

Excel variant array contents to file, with row contents joined

please bear with me as I'm very new to VBA, with prior experience primarily from Rhinoscript and other dedicated scripting options. The question is really very simple and I reckon someone can answer this very quickly, as I'm poor with arrays in VBA:
I have a spreadsheet where the objective is to import a number of values and text strings (resulting in some blanks) into e.g. A:L. This is done manually. I need to read these values into an array and then print them into a file so that each file row corresponds to a row of columns in the array. Currently I cannot seem to be able to convert the variant array into a string array (apparently necessary) and then join the subarrays into temporary arrays which are printed into the file. The following bit I've managed to scrape together results in a file output where each array value is on a single row, where as I'd like the contents of e.g. A1:L1 to be printed on single row.
Sub writerangetofile()
'Write data to a text file
'Declaring variables
Dim valarray() As Variant
Dim R As Long
Dim C As Long
'Set array as range
Sheet1.Activate
valarray() = Range("A1:L40")
'Setting the name and the path of text file based on workbook path
sFName = ThisWorkbook.Path & "\Output.txt"
'Get an unused file number
intFNumber = FreeFile
'Create a new file (or overwrite an existing one)
Open sFName For Output As #intFNumber
For R = 1 To UBound(valarray, 1) ' First array dimension is rows.
For C = 1 To UBound(valarray, 2) ' Second array dimension is columns.
Print #intFNumber, valarray(R, C)
Next C
Next R
'Close the text file
Close #intFNumber
End Sub
For simplicity as I've also not figured out how to obtain the last row with any content in it I've restricted the range to row 40 for now.
Any ideas on how to accomplish what I want elegantly? I've solved it by assigning single cells to variables, but I'd prefer to do it with an array. Ultimately I will later be interjecting a fixed text string after a recurring text string in the imported text, which is then followed by a numerical value obtained from a calculation.
Many thanks for any help and apologies for the ignorance.
In case you have any issues, this versions shows how to determine the last row and column
Option Explicit
Public Sub RangeToFile()
Dim ws As Worksheet, lr As Long, lc As Long, r As Long, c As Long
Dim arr As Variant, fName As String, fNumber As Long, txtLine As String
fName = ThisWorkbook.Path & "\Output.txt" 'File name & path based on workbook path
Set ws = Sheet1 'set a reference to main sheet
lr = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row 'find last row in column A
lc = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column 'find last column in row 1
arr = ws.Range(ws.Cells(1, "A"), ws.Cells(lr, lc)) 'Copy range to array
fNumber = FreeFile 'get next available file number assigned by windows
Open fName For Output As #fNumber 'create a new file, or overwrite an existing one
For r = 1 To UBound(arr, 1) '1st array dimension is rows
For c = 1 To UBound(arr, 2) '2nd array dimension is columns
txtLine = txtLine & arr(r, c) & ", " 'concatenate each cell in row, on a line
Next c 'the end of the row, moving to next one
txtLine = Left(txtLine, Len(txtLine) - 2) 'remove the extra comma at end of line
txtLine = txtLine & vbCrLf 'append a carriage return to the line
Next r
txtLine = Left(txtLine, Len(txtLine) - 2) 'remove carriage return at end of line
Print #fNumber, txtLine 'print entire text to the file with an extra carriage return
Close #fNumber 'close the text file
End Sub
and this one transposes columns to rows:
Public Sub RangeToFileColumnsToRows()
Dim ws As Worksheet, lr As Long, lc As Long, r As Long, c As Long
Dim arr As Variant, fName As String, fNumber As Long, txtLine As String
fName = ThisWorkbook.Path & "\Output.txt" 'File name & path based on workbook path
Set ws = Sheet1 'set a reference to main sheet
lr = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row 'find last row in column A
lc = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column 'find last column in row 1
arr = ws.Range(ws.Cells(1, "A"), ws.Cells(lr, lc)) 'Copy range to array
fNumber = FreeFile 'get next available file number assigned by windows
Open fName For Output As #fNumber 'create a new file, or overwrite an existing one
For c = 1 To UBound(arr, 2) '2nd array dimension is columns
For r = 1 To UBound(arr, 1) '1st array dimension is rows
txtLine = txtLine & arr(r, c) & ", " 'concatenate each cell in col, on a line
Next r 'the end of the col, moving to next one
txtLine = Left(txtLine, Len(txtLine) - 2) 'remove the extra comma at end of line
txtLine = txtLine & vbCrLf 'append a carriage return to the line
Next c
txtLine = Left(txtLine, Len(txtLine) - 2) 'remove carriage return at end of line
Print #fNumber, txtLine 'print entire text to the file
Close #fNumber 'close the text file
End Sub
Nevermind, I think I got a step forward:
For R = 1 To UBound(valarray, 1) ' First array dimension is rows.
For C = 1 To UBound(valarray, 2) ' Second array dimension is columns.
ReDim Preserve tvalarray(1 To C) 'Reset the array dimension on each iteration of loop whilst keeping results
'Input each single value into subarray for joining
tvalarray(C) = valarray(R, C)
'Join the subarray
jstring = Join(tvalarray)
Next C
ReDim Preserve jvalarray(1 To R) 'Reset the array dimension on each iteration of loop whilst keeping results
'Input the joined result into the new array
jvalarray(R) = jstring
'Print to file
Print #intFNumber, jvalarray(R)
Next R
This seems to do the job, I'll see if I run into pitfalls later.

parallel array for Visual Basic assignment

For this assignment I am being ask to "Read the file into parallel string arrays corresponding to the columns in the file".
The columns in the file are: "P_CODE" "P_DESCRIPT" "P_INDATE" "P_QOH" "P_MIN" "P_PRICE".
My question is "What is a parallel array?" and what does it look like. I took a guess at how I would imagine it might done but I'm not sure I'm on the right track.
========== project Inventory ==========
Selecting the Read button shall cause the contents of the
InventoryData.txt file to be read from your project’s default folder.
The file does contain a column header line.
Read the file into parallel string arrays corresponding to the columns in the file. After reading the file, close it. Display the
contents of the file in the parallel arrays, including column titles,
in the List box formatted using the Format() method. Left and right
align columns according to convention for their type of data. Hint:
Set the Font property of you List Box to a nonproportional space font
such as Courier.
Write the contents of the file in the parallel arrays, including
column titles, to a file named InventoryDataOut.txt created in your
project’s default folder. Delimit the columns with the pipe (|)
symbol. Each inventory item to one line of output to the file.
This is what I have done so far. I just started so this is not functioning code.
Private Sub btnRead_Click(sender As Object, e As EventArgs) Handles btnRead.Click
Dim dirPath As String = "C:\Users\...\Inventory\"
Dim filePath As String = dirPath & "InventoryData.txt"
'Arrays
Dim P_CODE As String()
Dim P_DESCRIPT As String()
Dim P_INDATE As String()
Dim P_QOH As String()
Dim P_MIN As String()
Dim P_PRICE As String()
' Open file for reading
Dim textIn As New StreamReader(
New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
'Read in the lines of text into the String variables in the array;
Dim i As Integer = 0
Do While textIn.Peek <> -1
Dim row As String = textIn.ReadLine
Dim columns As String() = row.Split(CChar(" "))
P_CODE(i) = columns(i)
P_DESCRIPT(i) = columns(i)
P_INDATE(i) = columns(i)
P_QOH(i) = columns(i)
P_MIN(i) = columns(i)
P_PRICE(i) = columns(i)
i = i + 1
Loop
'=====================================
'- After reading the file, close it.
textIn.Close()
I get the following warning and error (for each array) :
Warning 1 Variable 'P_CODE' is used before it has been assigned a
value. A null reference exception could result at
runtime.
Columns as independant arrays ? :/
One line answer : You're doing it right !
However :
there are simplier ways to do this (in terms of usage and readability - not performance)
there are typos in your code.
StackOverflow's main purpose is not to teach basic language reference, but help you overcome a specific problem with the appropriate coding (while keeping in mind an answer can't be an answer if it doesn't provide the elements to answer OP's question)
The error you stated is not really related to the question "what is a parallel array". "Variable used before it has been assigned a value". Of course ! Your array is Null. The variable is declared but no instance of it has been created. And Arrays are that strict in dotNet (unlike JavaScript)
What's your real question ?
It's more related to "how should I assign values to (parallel) array(s)" and could be reformulated like "How to read string values stored in a text file in parallel arrays and write them back ?"
Homework questions usually disallows the use of alternative approaches (and sometimes such homework questions are downvoted)
Different approaches like (using a database with linq or) :
Use File.ReadAllLines() to know the number of lines right from the beginning...
Declare your arrays top level : you won't be able to access them outside your btnRead Click otherwise.
Private P_CODE As String()
Private P_DESCRIPT As String()
' ...
Private P_PRICE As String()
Load the file content inside your btnRead Click...
Dim AllLines As String() = File.ReadAllLines(filePath)
' ^^ the above will open and close the file XD
Dim Columns As String()
' Initialize your arrays...
ReDim(P_CODE, AllLines.Length - 1)
ReDim(P_DESCRIPT, AllLines.Length - 1)
' ...
ReDim(P_PRICE, AllLines.Length - 1)
' set each cell value.
For LineIndex As Int32 = 0 To P_CODE.Length - 1
Columns = AllLines(LineIndex).Split(" "c)
P_CODE(LineIndex) = Columns(0)
P_DESCRIPT(LineIndex) = Columns(1)
' ...
P_PRICE(LineIndex) = Columns(5)
' Hey ! Your File Datas are ordered horizontaly,
' so Columns must be indexed from 0 to 5
' instead of you "i" in your code in order
' to correctly feed your parallel arrays
Next
Your teacher wanna force you to open the file using StreamReader and teach you to not forget to close the file... and use StreamReader.ReadLine() ... Okay..!
' ...
Dim LineIndex As Int32 = 0
Dim textIn As New StreamReader(filePath)
' ^^ why do you bother using a FileStream ?
Dim Row As String = textIn.ReadLine()
Dim Columns As String()
Do While row IsNot Nothing
Redim Preserve P_CODE(LineIndex)
Redim Preserve P_DESCRIPT(LineIndex)
' ...
Redim Preserve P_PRICE(LineIndex)
Columns = Row.Split(" "c)
P_CODE(LineIndex) = Columns(0)
P_DESCRIPT(LineIndex) = Columns(1)
' ...
P_PRICE(LineIndex) = Columns(5)
Row = textIn.ReadLine()
LineIndex = LineIndex + 1
Loop
textIn.Close() ' Voila !
' /!\ Add at least a Try Catch enclosing your Do/Loop if you're lazy.
Tell your teacher you're gonna use Using because he forces you to use StreamReader.Peek() (is .Peek() necessary ?)
Using textIn As New StreamReader(filePath)
Dim LineIndex As Int32 = 0
Dim Columns As String()
Do While textIn.Peek() <> -1
Redim Preserve P_CODE(LineIndex)
Redim Preserve P_DESCRIPT(LineIndex)
' ...
Redim Preserve P_PRICE(LineIndex)
Columns = textIn.ReadLine().Split(" "c)
P_CODE(LineIndex) = Columns(0)
P_DESCRIPT(LineIndex) = Columns(1)
' ...
P_PRICE(LineIndex) = Columns(5)
LineIndex = LineIndex + 1
Loop
End Using ' StreamReader closed ! XD
No ! Don't ask me to use a FileStream ! I would if I know the size of each data chunck in the file and I'm using structures of fixed size. FileStream is best used when manipulating large buffer of datas as it directly works in memory with appropriate asynchronous checks.
Just to read lines ? No ! For this part, read the documentation.
String.Format()
Dim Row As String
MyListBox.Items.Clear()
For LineIndex As Int32 = 0 To P_CODE.Length - 1
Row = String.Format("{0, -12}{1,-16}{2,10}{3,10}{4,8}{5,10}", _
P_CODE(LineIndex), _
P_DESCRIPT(LineIndex), _
P_INDATE(LineIndex), _
P_QOH(LineIndex), _
P_MIN(LineIndex), _
P_PRICE(LineIndex))
MyListBox.Items.Add(Row)
Next
Write file to InventoryDataOut.txt..
Dim Separator As String = "|"
Dim FileContent As New StringBuilder() ' System.Text
' Prepare File content...
If P_CODE.Length > 0 Then
For LineIndex As Int32 = 0 To P_CODE.Length - 2 ' Note the -2
FileContent.AppendLine(String.Format("{0}{6}{1}{6}{2}{6}{3}{6}{4}{6}{5}", _
P_CODE(LineIndex), _
P_DESCRIPT(LineIndex), _
P_INDATE(LineIndex), _
P_QOH(LineIndex), _
P_MIN(LineIndex), _
P_PRICE(LineIndex), _
Separator)))
Next
FileContent.Append(String.Format("{0}{6}{1}{6}{2}{6}{3}{6}{4}{6}{5}", _
P_CODE(P_CODE.Length - 1), _
P_DESCRIPT(P_CODE.Length - 1), _
P_INDATE(P_CODE.Length - 1), _
P_QOH(P_CODE.Length - 1), _
P_MIN(P_CODE.Length - 1), _
P_PRICE(P_CODE.Length - 1), _
Separator)))
' This ensures we don't add unnecessary line at the end of the file
' which would make your application explode !
' best move is to check for null entries upon file parsing.
' (Again : File.ReadAllLines() + RemoveEmptyString parameter to keep it simple)
End If
' Create your file using the stream object
' you're forced to use by your teacher,
' then dump the the content of your StringBuilder inside like
MyDummyStream.Write(FileContent.ToString())
' And close you dummy stream or use Using XD
Does your teacher has a bunch of CSV-like files which needs separator conversion from space to pipe ?
It seems he's just lazy to write the code to dispatch each column in several parallel arrays for display or other purpose. Or why would you use the space as separator upon loading the content of your file while using pipe instead to write them ?
If its a typo, just don't bother reading this. Otherwise, your teacher is just asking you to create a small program that converts the space separator to pipe... (while giving you the mission to learn manipulate (parallel) arrays, streams I/O, and String.Format)
.

Resources