Read a text file

Read a text file

Often the output of one program is used as the input of another; a common method of transferring this information is through the use of files. For instance, an industrial designer may make a clay concept model of a new product; to begin the digital modeling process the model may be 3D scanned or measured with a coordinate measuring machine (CMM). The scanner or CMM doesn't need to know anything about the CAD program that will be used, it can output point data to a file which can then be imported to the CAD software as "point clouds" and subsequently used to recreate the surfaces. This tutorial will show how to read a text file and parse point information from it to draw lines between the given points.

File types

On Windows systems file names take the form [filename].[extension]; generally the extension is 3 letters long (though the filesystem your OS version uses may allow more) and they help the OS and the user to quickly identify the type of file. Even though there are 100's of different file types, they all basically belong to one of two categories: text file or binary file. Typical text files you will run across include: .txt (obviously), .log, .csv, .x_t. Some common binary files include: .exe, .dll, .mp3, .jpg, .prt. Try opening a few different files in notepad (or your favorite text editor, but remember to make a backup first and DO NOT SAVE THE FILE IN THE TEXT EDITOR); if you can make sense of it, it is likely a text file - if you are confronted with a mass of seeming gibberish, it is either a binary file or monkey test subject #27649's attempt at rewriting Shakespear's Hamlet. So even though we are reading text files, we are not limited to only files with the .txt extension.

.NET StreamReader

.NET provides everything we need to read text files; specifically, we will be using a "stream reader" object. For our input file, each line will represent a single point; X, Y, and Z values separated by commas.The stream reader will allow us to read the input file line by line. Our strategy will be to read a line, split it into components (XYZ), assign the components to a corresponding Point3D, and on the second and subsequent points - draw a line from the previous point to the current point.

Download the code here

[vbnet]'NXJournaling.com
'example: read text file
'each line of the input file should be 3 numbers separated by commas (#.###, #.###, #.###)
'the numbers will be interpreted as line start/end points

Option Strict Off
Imports System
Imports System.IO
Imports System.Windows.Forms
Imports NXOpen

Module Module1

Sub Main()
Dim openFileDialog1 As New OpenFileDialog()

openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"
openFileDialog1.FilterIndex = 1
openFileDialog1.RestoreDirectory = True

If openFileDialog1.ShowDialog() = DialogResult.OK Then
Dim theSession As Session = Session.GetSession()
Dim workPart As Part = theSession.Parts.Work
Dim line As String
Dim startPoint As Point3d = nothing
Dim endPoint As Point3d
Dim firstPass as Boolean = True
Dim delim As Char() = {","c}

Using sr As StreamReader = New StreamReader(openFileDialog1.FileName)
Try
line = sr.ReadLine()
While Not line Is Nothing
Dim strings As String() = line.Split(delim)
endPoint.x = Double.Parse(strings(0))
endPoint.y = Double.Parse(strings(1))
endPoint.z = Double.Parse(strings(2))
If firstPass Then
firstPass = False
Else
'create a line from startpoint to endpoint
workPart.Curves.CreateLine(startPoint, endPoint)
End If
startPoint = endPoint

line = sr.ReadLine()
End While
Catch E As Exception
MessageBox.Show(E.Message)
End Try
End Using
End If
End Sub

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image when the NX session terminates
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.AtTermination

End Function

End Module [/vbnet]

Some items of note:
You may have noticed we didn't use the usual Dim statement before the StreamReader variable. The Using... End Using block will dispose of the stream reader object at the proper time; otherwise, we would have to call the stream reader's Close method ourselves to mark it for garbage collection. The Using... End Using block allows the compiler to do the work for us and we can be assured the stream reader will be disposed of properly; had we used Dim sr as StreamReader... and forgotten the sr.Close statement, the compiler would not warn us (since it doesn't know when or if we want to close the stream reader).

The Double.Parse method is used to convert the text to a numeric value (of type double). If your computer's regional settings use something other than the decimal point for the decimal separator (the comma, for instance), you likely got an error message along the lines of "input in an incorrect format". We can correct this by specifying what format we want to read; the next bit of journal code will show how to do this.

All input points were interpreted into the absolute coordinate system of the part. The next journal will include a function to map the points to the WCS.

Improved Code

This next journal includes a System.Globalization.CultureInfo object that is set to US english ("en-US"), since that is the format that we expect the input strings to use (change the journal accordingly for your specific requirements). Now when the values are parsed, we specify the text to parse along with the culture info to interpret it properly. This code should now run properly regardless of the computer's regional settings.

Lastly, there is a function included (taken from a GTAC example) that converts the point coordinates to the WCS.

[vbnet]
'NXJournaling.com
'example: read text file
'each line of the input file should be 3 numbers separated by commas (#.###, #.###, #.###)
'the numbers will be interpreted as line start/end points

Option Strict Off
Imports System
Imports System.IO
Imports System.Windows.Forms
Imports NXOpen
Imports NXOpen.UF

Module Module1
Dim ufs As UFSession = UFSession.GetUFSession

Sub Main()
Dim openFileDialog1 As New OpenFileDialog()

openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*"
openFileDialog1.FilterIndex = 1
openFileDialog1.RestoreDirectory = True

If openFileDialog1.ShowDialog() = DialogResult.OK Then
Dim theSession As Session = Session.GetSession()
Dim workPart As Part = theSession.Parts.Work
Dim line As String
Dim startPoint As Point3d = nothing
Dim endPoint As Point3d
Dim i As Integer = 0
Dim firstPass as Boolean = True
Dim delim As Char() = {","c}
Dim USculture As system.globalization.CultureInfo = New System.Globalization.CultureInfo("en-US")

Using sr As StreamReader = New StreamReader(openFileDialog1.FileName)
Try
line = sr.ReadLine()
While Not line Is Nothing
Dim strings As String() = line.Split(delim)
endPoint.x = Double.Parse(strings(0), USculture)
endPoint.y = Double.Parse(strings(1), USCulture)
endPoint.z = Double.Parse(strings(2), USCulture)
endPoint = Abs2WCS(endPoint)
If firstPass Then
firstPass = False
Else
'create a line from startpoint to endpoint
workPart.Curves.CreateLine(startPoint, endPoint)
End If
startPoint = endPoint
line = sr.ReadLine()
End While
Catch E As Exception
MessageBox.Show(E.Message)
End Try
End Using
End If

End Sub
'&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
'Date: 11/18/2010
'Subject: Sample NX Open .NET Visual Basic routine : map point from absolute to wcs
'
'Note: function taken from GTAC example code

Function Abs2WCS(ByVal inPt As Point3d) As Point3d
Dim pt1(2), pt2(2) As Double

pt1(0) = inPt.X
pt1(1) = inPt.Y
pt1(2) = inPt.Z

ufs.Csys.MapPoint(UFConstants.UF_CSYS_ROOT_COORDS, pt1, _
UFConstants.UF_CSYS_ROOT_WCS_COORDS, pt2)

Abs2WCS.X = pt2(0)
Abs2WCS.Y = pt2(1)
Abs2WCS.Z = pt2(2)

End Function
'&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image when the NX session terminates
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.AtTermination

End Function

End Module
[/vbnet]

Conclusion

This article shows how to use .NET's stream reader to read a text file line by line. As long as you know how to interpret the input file, you can write custom code to read it and do something useful with it in NX.

Comments

NXJournaling,

Do you have a code where the whole input file is "loaded" to a string (then closed) rather than read line by line. My code below (for an xls macro) does not work

'Allocate all the data in file to string 'TotalFile' then close it
FileNum = FreeFile
Open strFileToProcess For Binary As #FileNum
TotalFile = Space(LOF(FileNum))
Get #FileNum, , TotalFile
Close #FileNum

Thanks
Regards

Are you reading a text file or a binary file?

The following links may be of some interest to you. The stream reader object has several "read*" methods, one or more may be useful to you. The other link has some example code for reading and writing to a binary file.

https://msdn.microsoft.com/en-us/library/System.IO.StreamReader%28v=vs.1...

https://msdn.microsoft.com/en-us/library/aa711083%28v=vs.71%29.aspx

Thanks for the links. Apologies for the stupid question. I totally missed the ReadToEnd() option when looking at the online doc a couple of days ago. Doing too many things at once I guess (!). The following does job

If openFileDialog1.ShowDialog() = DialogResult.OK Then
Dim sr As StreamReader = New StreamReader(openFileDialog1.FileName)
TotalFile=sr.ReadToEnd()
sr.Close
theLW.WriteLine("File content is: " & TotalFile)
End If

Thanks
Regards

You don't really need a Streamreader. Just use the System.IO.File read functions. ReadAllText reads all text into a single string, and ReadAllLines reads all text into an array of strings, one string per line.

Back in the NX programming world after doing some "real" work. I have started looking into a way pf processing a number of .csv files (in 1-go) to create records in an NX(.CAE) .afu file

Does anyone know if there is a way of extracting an entire column from a csv file without having to read it line-by line?

To create a record in an NX .afu file one needs to create "vectors" of X and Y values. In my case the Y values may actually be a set of values so the Y column is actually 2 columns.

But consider the simple case of an X,Y set. The .csv file will have the standard format:

header1,header2, header3, etc
value11,value21,value31,etc
value12,value22,value32,etc
etc

read csv file to create columns of data

Dim Xdata() As Double
Dim Ydata() As Double
Dim theAFUData As CAE.AfuData

Xdata:= csvcolumn1 (from row 2 to Nrow)

For col = 2 to NCol
Ydata=csvcolumn(col)
'create record afu record
theAFUData.SetRealData(Xdata,Ydata)
Next col

Thanks
Regards

Thanks
Regards

Would the Method Array.Copy() works in NX? Looks like on needs to declare the System.Array and can't remember if this is "supported". It's been a while since I have a look at NX programming

Thanks
Regards

If you are using a .NET language, it should be supported.
https://msdn.microsoft.com/en-us/library/k4yx47a1(v=vs.110).aspx

I don't know of a way to get what you want without reading every line, but the TextFieldParser may help with your task.

https://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.te...

while doing some search onthe problem I came across the use of DataTable variable which seems to be using the system.data.olddb
could this be used?

i have nearly work out the workflow to convert the csv into the array required. just need to start basic coding

Thanks
Regards

Found some pseudo-code to convert csv to a DataTable, see below.
Has anyone ever used that sort of type? Looks like it's quite neat
Haven't found yet how to
1. create/put data in the table as double
2. extract an entire column (or a column between 2 specific row)
3. "print" a specific value or loop through data in a column/row

Thanks
Regards


Sub TestDataTable()

'converts the CSV into a new in-memory DataTable
Dim sFileName As String
sFileName = "C:\exampletestdata.txt"

Dim TextFileReader As New Microsoft.VisualBasic.FileIO.TextFieldParser(sFileName)

TextFileReader.TextFieldType = FileIO.FieldType.Delimited
TextFileReader.SetDelimiters(",")

Dim dtTextFileTable As DataTable = Nothing

Dim Column As DataColumn
Dim Row As DataRow
Dim UpperBound,ColumnCount As Int32
Dim CurrentRow As String()

Dim sDataTableName As String
sDataTableName = "TextFileTable"

While Not TextFileReader.EndOfData
Try
CurrentRow = TextFileReader.ReadFields()
If Not CurrentRow Is Nothing Then
''# Check if DataTable has been created
If dtTextFileTable Is Nothing Then
dtTextFileTable = New DataTable(sDataTableName)
''# Get number of columns
UpperBound = CurrentRow.GetUpperBound(0)
''# Create new DataTable
For ColumnCount = 0 To UpperBound
Column = New DataColumn()
Column.DataType = System.Type.GetType("System.String")
Column.ColumnName = "Column" & ColumnCount
Column.Caption = "Column" & ColumnCount
Column.ReadOnly = True
Column.Unique = False
dtTextFileTable.Columns.Add(Column)
Next
End If
Row = dtTextFileTable.NewRow
For ColumnCount = 0 To UpperBound
Row("Column" & ColumnCount) = CurrentRow(ColumnCount).ToString
Next
dtTextFileTable.Rows.Add(Row)
End If
Catch ex As _
Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message & _
"is not valid and will be skipped.")
End Try
End While
TextFileReader.Dispose()

End sub

Thanks
Regards

Have a look at this code:
http://nxjournaling.com/content/find-duplicate-sheet-bodies

It uses a data table to record information about model faces. It should answer your question #1.

Once you have your information in the data table, you can run SQL code on it to extract all the rows, or only certain ones that meet your specified criteria. Do a websearch on "SQL tutorial" and you should get many good results.

Thanks for the link and suggestion

Made some progress. I just cannot decide if processing the csv file with a DataTable is better/faster that the "traditional" way

I have not found a way of extracting an entire column to put it into an array without using LINQ. Looks like one still needs to loop trough each row which defeat the purpose somewhat

Dim X(),Y() As Double
Dim total As Integer
irowtotal = myTableData.Rows.Count - 1

ReDim X(0 To irowtotal)
ReDim Y(0 To irowtotal)

For N = 0 to 15 By 3
For r = 0 To irowtotal
X(r) = myTableData.Rows(r)(N)
Y(r) = myTableData.Rows(r)(N+1)
Next r
'deal with X& Y
Next N

Thanks
Regards