Add multiple elements to List - c#

for background: I have the following code to recursively search for files through a directory, which returns a list of all documents. I want to have the foldername and the documentname.
So far so good. It works well as long as I want to write two elements to my List.
I have a class Companyentry;
class Companyentry
{
public string Entry { get; set; }
public string Folder { get; set; }
public Companyentry(string ey, string fd)
{
Entry = ey;
Folder = fd;
}
}
And my code to write to the List:
List<string> companylist = new List<string>();
private async void button4_Click(object sender, EventArgs e)
{
// ListView initialisierem
listView1.Columns.Add("Dateiname", 200, HorizontalAlignment.Left);
listView1.Columns.Add("Unternehmen", 20, HorizontalAlignment.Left);
listView1.CheckBoxes = true;
listView1.Sorting = SortOrder.Ascending;
DirectoryInfo di = new DirectoryInfo(rootfolder);
Console.WriteLine("No search pattern returns:");
//Write to List
foreach (var fi in di.GetFiles("*", SearchOption.AllDirectories))
{
// Entries
string[] Split = (fi.Directory.Name).Split(new Char[] { '_' });
strEntry = (Split[1]);
companylist.Add(new Companyentry("Entry", "Folder"));
}
I got the following error that "Argument 1 cannot convert from Companyentry to 'string' ":
Issue Screenshot
Whats wrong?

Because this line is wrong:
companylist.Add(new Companyentry("Entry", "Folder"));
Change to this:
companylist.Add("Entry", "Folder");
new Companyentry("Entry", "Folder");
List is a string list.

Related

C# Adding an array or list into an List

I've got a List of Document
public class Document
{
public string[] fullFilePath;
public bool isPatch;
public string destPath;
public Document() { }
public Document(string[] fullFilePath, bool isPatch, string destPath)
{
this.fullFilePath = fullFilePath;
this.isPatch = isPatch;
this.destPath = destPath;
}
The fullFilepath should a List or an Array of Paths.
For example:
Document 1
---> C:\1.pdf
---> C:\2.pdf
Document 2
---> C:\1.pdf
---> C:\2.pdf
---> C:\3.pdf
etc.
My problem if I am using an array string all Documents got "null" in its fullFilePath.
If I'm using a List for the fullFilePath all Documents got the same entries from the last Document.
Here is how the List is filled:
int docCount = -1;
int i = 0;
List<Document> Documents = new List<Document>();
string[] sourceFiles = new string[1];
foreach (string file in filesCollected)
{
string bc;
string bcValue;
if (Settings.Default.barcodeEngine == "Leadtools")
{
bc = BarcodeReader.ReadBarcodeSymbology(file);
bcValue = "PatchCode";
}
else
{
bc = BarcodeReader.ReadBacrodes(file);
bcValue = "009";
}
if (bc == bcValue)
{
if(Documents.Count > 0)
{
Array.Clear(sourceFiles, 0, sourceFiles.Length);
Array.Resize<string>(ref sourceFiles, 1);
i = 0;
}
sourceFiles[i] = file ;
i++;
Array.Resize<string>(ref sourceFiles, i + 1);
Documents.Add(new Document(sourceFiles, true,""));
docCount++;
}
else
{
if (Documents.Count > 0)
{
sourceFiles[i] = file;
i++;
Array.Resize<string>(ref sourceFiles, i + 1);
Documents[docCount].fullFilePath = sourceFiles;
}
}
}
You are using the same instance of the array for every document. The instance is updated with a new list of files at every inner loop, but an array is a reference to an area of memory (oversimplification, I know but for the purpose of this answer is enough) and if you change the content of that area of memory you are changing it for every document.
You need to create a new instance of the source files for every new document you add to your documents list. Moreover, when you are not certain of the number of elements that you want to be included in the array, it is a lot better to use a generic List and remove all that code that handles the resizing of the array.
First change the class definition
public class Document
{
public List<string> fullFilePath;
public bool isPatch;
public string destPath;
public Document() { }
public Document(List<string> fullFilePath, bool isPatch, string destPath)
{
this.fullFilePath = fullFilePath;
this.isPatch = isPatch;
this.destPath = destPath;
}
}
And now change your inner loop to
foreach (string file in filesCollected)
{
string bc;
string bcValue;
....
if (bc == bcValue)
{
List<string> files = new List<string>();
files.Add(file);
Documents.Add(new Document(files, true, ""));
docCount++;
}
else
Documents[docCount].fullFilePath.Add(file);
}
Notice that when you need to add a new Document I build a new List<string>, add the current file and pass everything at the constructor (In reality this should be moved directly inside the constructor of the Document class). When you want to add just a new file you could add it directly to the public fullFilePath property
Moving the handling of the files inside the Documents class could be rewritten as
public class Document
{
public List<string> fullFilePath;
public bool isPatch;
public string destPath;
public Document()
{
// Every constructory initializes internally the List
fullFilePath = new List<string>();
}
public Document(string aFile, bool isPatch, string destPath)
{
// Every constructory initializes internally the List
fullFilePath = new List<string>();
this.fullFilePath.Add(aFile);
this.isPatch = isPatch;
this.destPath = destPath;
}
public void AddFile(string aFile)
{
this.fullFilePath.Add(aFile);
}
}
Of course, now in you calling code you pass only the new file or call AddFile without the need to check for the list initialization.
The issue should be here:
string[] sourceFiles = new string[1];
If you move this line of code in your foreach you should solve this problem because in your foreach you always use the same variable, so the same reference.
int docCount = -1;
int i = 0;
List<Document> Documents = new List<Document>();
foreach (string file in filesCollected)
{
string[] sourceFiles = new string[1];
string bc;
string bcValue;
if (Settings.Default.barcodeEngine == "Leadtools")
{
bc = BarcodeReader.ReadBarcodeSymbology(file);
bcValue = "PatchCode";
}
else
{
bc = BarcodeReader.ReadBacrodes(file);
bcValue = "009";
}
if (bc == bcValue)
{
if(Documents.Count > 0)
{
Array.Clear(sourceFiles, 0, sourceFiles.Length);
Array.Resize<string>(ref sourceFiles, 1);
i = 0;
}
sourceFiles[i] = file ;
i++;
Array.Resize<string>(ref sourceFiles, i + 1);
Documents.Add(new Document(sourceFiles, true,""));
docCount++;
}
else
{
if (Documents.Count > 0)
{
sourceFiles[i] = file;
i++;
Array.Resize<string>(ref sourceFiles, i + 1);
Documents[docCount].fullFilePath = sourceFiles;
}
}
}

C# - Extracting satellite two line elements from a text file

I'm very new to c# and programming in general so I apologise if this doesn't make sense...
I need to be able to search a textbox or combobox to read a notepad file containing many satellite two line element codes.
The text file is set out like this:
0 VANGUARD 1
1 00005U 58002B 14242.42781498 .00000028 00000-0 24556-4 0 2568
2 00005 034.2497 271.4959 1848458 183.2227 175.4750 10.84383299975339
0 TRANSIT 2A
1 00045U 60007A 14245.43855606 .00000265 00000-0 95096-4 0 2208
2 00045 066.6958 193.0879 0251338 053.7315 060.2264 14.33038972819563
0 EXPLORER 11
1 00107U 61013A 14245.36883128 .00001088 00000-0 12832-3 0 1217
2 00107 028.7916 229.2883 0562255 219.9933 302.0575 14.05099145667434
Etc.
I need to search the box for the only satellite's name (the name after the 0 in the 'first' row) and extract that name into another textbox, and to use in my code. Additionally, I need to seperately extract the 2 lines of code directly beneath the name selected in the box (also to use in the code).
I have written code to use these two line elements, but I'm not able to automatically put them in my code.
Thank you
here is something that you can try quick and dirty that I have come up with.
1st place the file in a folder on your local hard drive.
2nd where I have filepath defined replace it with your actual file path and know how to use the # symbol and what it means in C#
3rd notice how I used the string .Replace Method.. you will have to tweak it I just gave you an Idea I am not going to write the entire code for you.. good luck.
static void Main(string[] args)
{
var fileName = string.Empty;
var filePath = #"C:\Users\myfolder\Documents\RGReports\"; //for testing purposes only
List<string> listNames = new List<string>();
string[] filePaths = Directory.GetFiles(#filePath);
foreach (string file in filePaths)
{
if (file.Contains(".txt"))
{
fileName = file;
using (StreamReader sr = File.OpenText(fileName))
{
//string s = String.Empty;
var tempFile = sr.ReadToEnd();
var splitFile = tempFile.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
foreach (string str in splitFile)
{
if (str.Length == 12)
{
listNames.Add(str.Substring(0, str.Length).Replace("0", "").Replace("1", "").Replace("2A",""));
}
Console.WriteLine(str);
}
}
}
}
}
Results will yield the following for names for example tested in a Console App
VANGUARD
TRANSIT
EXPLORER
You can use regular expressions for this task.
(I assume that the letters/numbers block after the name is also part of the name)
This code will do the following:
capture the name of the satellite and the two lines into a Satellite object
populate a ComboBox with the names of the satellite
whenever you selected a satellite you can know which one it was
a search box that searches for the first satellite beginning with the typed text
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
private readonly BindingList<Satellite> _satellites = new BindingList<Satellite>();
private string _input = #"
0 VANGUARD 1
1 00005U 58002B 14242.42781498 .00000028 00000-0 24556-4 0 2568
2 00005 034.2497 271.4959 1848458 183.2227 175.4750 10.84383299975339
0 TRANSIT 2A
1 00045U 60007A 14245.43855606 .00000265 00000-0 95096-4 0 2208
2 00045 066.6958 193.0879 0251338 053.7315 060.2264 14.33038972819563
0 EXPLORER 11
1 00107U 61013A 14245.36883128 .00001088 00000-0 12832-3 0 1217
2 00107 028.7916 229.2883 0562255 219.9933 302.0575 14.05099145667434
";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var regexObj =
new Regex(#"(?<=^\d+\s(?<name>[\w|\d|\s]+))\r\n(?<line1>(?<=^).*)\r\n(?<line2>(?<=^).*(?=\r))",
RegexOptions.Multiline);
Match matchResult = regexObj.Match(_input);
while (matchResult.Success)
{
string name = matchResult.Groups["name"].Value;
string line1 = matchResult.Groups["line1"].Value;
string line2 = matchResult.Groups["line2"].Value;
var regexObj1 = new Regex(#"(?<=^[1|2].*)([\d\w.-]+)");
Match matchResult1 = regexObj1.Match(line1);
var numbers1 = new List<string>();
while (matchResult1.Success)
{
string s = matchResult1.Value;
numbers1.Add(s);
matchResult1 = matchResult1.NextMatch();
}
var regexObj2 = new Regex(#"(?<=^[1|2].*)([\d\w.-]+)");
Match matchResult2 = regexObj2.Match(line2);
var numbers2 = new List<string>();
while (matchResult2.Success)
{
string s = matchResult2.Value;
numbers2.Add(s);
matchResult2 = matchResult2.NextMatch();
}
_satellites.Add(new Satellite
{
Name = name,
Line1 = line1,
Line2 = line2,
Numbers1 = numbers1,
Numbers2 = numbers2
});
matchResult = matchResult.NextMatch();
}
comboBox1.DataSource = _satellites;
comboBox1.DisplayMember = "Name";
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var comboBox = (ComboBox) sender;
var satellites = comboBox.DataSource as List<Satellite>;
if (satellites != null && comboBox.SelectedIndex > -1)
{
Satellite selectedSatellite = satellites[comboBox.SelectedIndex];
Console.WriteLine("Selected satellite: " + selectedSatellite.Name);
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
var textBox = (TextBox) sender;
string text = textBox.Text;
if (!string.IsNullOrWhiteSpace(text))
{
Satellite satellite =
_satellites.FirstOrDefault((s => s.Name.ToLower().StartsWith(text.ToLower())));
if (satellite != null)
{
Console.WriteLine("Found satellite: " + satellite);
}
}
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
var textBox = (TextBox) sender;
string text = textBox.Text;
if (!string.IsNullOrWhiteSpace(text))
{
Satellite satellite =
_satellites.FirstOrDefault(
s => s.Numbers1.Any(t => t.StartsWith(text)) || s.Numbers2.Any(t => t.StartsWith(text)));
if (satellite != null)
{
Console.WriteLine("Found satellite: " + satellite);
}
}
}
}
internal class Satellite
{
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public List<string> Numbers1 { get; set; }
public List<string> Numbers2 { get; set; }
public override string ToString()
{
return string.Format("Name: {0}", Name);
}
}
}
Result:
If you don't want to use regex you could do something like this:
public List<string> GetSatelliteNames(string input)
{
string[] split = input.split(new string[2] { "\n", "\r\n" });
List<string> result = new List<string>();
foreach (var s in split)
{
string splitagain = s.split(new char[1] { ' ' });
if (s[0] == "0") result.add(s[1]);
}
return result;
}

Reading specific lines in a .Log file

I have a log file that I am reading into different objects. One object starts at a Line that contains the words "Announce message" and the following lines contain the data that belongs to that message. This entry stops at a line that contains the word "Disposed".
I want to read all the data from between these 2 lines that, contains certain words.
Im currently using a Dictionary because the line with "Announce message" also contains a UID but the following lines contain the data for that UID.
How would you do that?
This is what i have come up with so far.
public static void P2PLogParser(List<FileInfo> fileList)
{
foreach (FileInfo fi in fileList)
{
//Læser alle linier i csv fil
foreach (var line in File.ReadAllLines(fi.FullName))
{
string MeterUID = GetMeterUID(line);
string MimHashcode = GetMimHashcode(line);
string FirmwareUploadStatus = GetFirmwareUploadStatus(line);
string IsKnown = GetIsKnown(line);
DateTime P2PTimeStamp = GetTimestamp(line);
if (IsMeterEntry(line) && !meters.ContainsKey(MeterUID))
{
string MeterNr = GetMeterUID(line).Replace("4B414D", "");
int meternr = int.Parse(MeterNr, System.Globalization.NumberStyles.HexNumber);
meters.Add(MeterUID, new Meter()
{
MeterUID = MeterUID,
MeterNR = meternr,
P2Pmeterentry = new List<P2PMeterEntry>()
});
}
if (IsMeterEntry(line))
{
P2PMeterEntry p2pmeter = new P2PMeterEntry
{
P2PTimeStamp = P2PTimeStamp,
MimHashcode = MimHashcode,
FirmwareUploadStatus = FirmwareUploadStatus,
IsKnown = IsKnown,
P2PMetersession = new List<P2PMeterSession>()
};
if (IsNoLongerMeterEntry(line))
{
string SessionLevel = GetLevel(line);
string SessionMessage = GetSessionMessage(line);
string Context = GetSessionContext(line);
P2PMeterSession MeterSession = new P2PMeterSession
{
SessionTimeStamp = P2PTimeStamp,
SessionLevel = SessionLevel,
SessionMessage = SessionMessage,
Context = Context
};
meterSession.Add(MeterSession);
}
meters[MeterUID].P2Pmeterentry.Add(p2pmeter);
}
}
}
}
and the IsMeterEntry and IsNoLongerMeterEntry
//IsMeterSession
public static bool IsMeterEntry(string text)
{
return text.ToLower().Contains("announce message received:");
}
public static bool IsNoLongerMeterEntry(string text)
{
return text.ToLower().Contains("context - disposed");
}
Implement a simple state machine with two states: IgnoreLine (initial state) and Announce.
for each line in log
if line contains "Announce message"
read UID
create a StringBuilder
set state=Announce
else if line contains "Disposed"
store the StringBuilder's content in the dictionary[uid]
set state=IgnoreLine
else if state==Announce and line contains "certain words"
append line to StringBuilder

Using word interop create multiple documents from a template for print preview

I have an app where the user selects from a list all the students they want to print an award document for.
I have a template .doc file that contains 3 text boxes that I populate from code. I can populate the file and show it in the print preview for a single student, but based on how many students are selected I want to create a large document with many pages I can see in print preview before printing and print all at once.
The following is my attempt to turn my working code for a single word document being created from the template to show in print preview. Any ideas?
public void AddStudentToDocument(IEnumerable<StudentToPrint> studentsToPrint )
{
_Application oWordApp = new Application();
string folder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
string specificFolder = Path.Combine(folder, "FoothillsAcademy");
string fileLocation = Path.Combine(specificFolder, "CertTemplate.doc");
if (File.Exists(fileLocation))
{
var oWordDoc = oWordApp.Documents.Open(fileLocation);
oWordDoc.Activate();
oWordApp.Selection.TypeParagraph();
foreach (var studentToPrint in studentsToPrint)
{
_Document oDoc = oWordApp.Documents.Add();
Selection oSelection = oWordApp.Selection;
string docText = oWordDoc.Content.Text;
if (docText != null)
{
int boxNumber = 1;
foreach (Shape shape in oWordApp.ActiveDocument.Shapes)
{
if (shape.Type == Microsoft.Office.Core.MsoShapeType.msoTextBox)
{
if (boxNumber == 1)
{
shape.TextFrame.TextRange.Text = studentToPrint.StudentName;
}
if (boxNumber == 2)
{
shape.TextFrame.TextRange.Text = studentToPrint.Rank;
}
if (boxNumber == 3)
{
shape.TextFrame.TextRange.Text = studentToPrint.DateAcheved;
}
boxNumber++;
}
}
_Document oCurrentDocument = oWordApp.Documents.Add(oWordDoc);
copyPageSetup(oCurrentDocument.PageSetup, oDoc.Sections.Last.PageSetup);
oCurrentDocument.Range().Copy();
oSelection.PasteAndFormat(WdRecoveryType.wdFormatOriginalFormatting);
//if (!Object.ReferenceEquals(oWordDoc.Content, oWordDoc.Last()))
oSelection.InsertBreak(WdBreakType.wdSectionBreakNextPage);
}
oWordApp.Visible = true;
oWordApp.ShowStartupDialog = true;
oWordApp.ActiveDocument.PrintPreview();
}
}
}
private void copyPageSetup(PageSetup source, PageSetup target)
{
target.PaperSize = source.PaperSize;
if (!source.Orientation.Equals(target.Orientation))
target.TogglePortrait();
target.TopMargin = source.TopMargin;
target.BottomMargin = source.BottomMargin;
target.RightMargin = source.RightMargin;
target.LeftMargin = source.LeftMargin;
target.FooterDistance = source.FooterDistance;
target.HeaderDistance = source.HeaderDistance;
target.LayoutMode = source.LayoutMode;
}
public class StudentToPrint
{
public string StudentName { get; set; }
public string Rank { get; set; }
public string DateAcheved { get; set; }
}
Currently I am testing this with a collection of StudentsToPrint added below. Based on the data below I would expect to see 3 certificates personalized for each of the 3 students. Each certificate would be on its own page.
List<StudentToPrint> listOfStudents = new List<StudentToPrint>
{
new StudentToPrint
{
DateAcheved = DateTime.Now.ToShortDateString(),
Rank = "5th Degree",
StudentName = "Scott LaFoy"
},
new StudentToPrint
{
DateAcheved = DateTime.Now.ToShortDateString(),
Rank = "3rd Degree",
StudentName = "John Doe"
},
new StudentToPrint
{
DateAcheved = DateTime.Now.ToShortDateString(),
Rank = "2nd Degree",
StudentName = "Jane Doe"
}
};
The template is a word doc that has 3 text boxes. Using the text box lets me set font and position different for each of them as well as making the background transparent so the template background shows through. I am sure there is another way to do this as well but there is not much on this topic around.

Pull separate columns from .csv into separate arrays in c#

Background on this project. It started as a simple homework assignment that required me to store 5 zip codes and their corresponding cities. When a user puts a Zip code in a textbox, a corresponding city is returned, and likewise the opposite can be done. I wrote the code to return these values, but then I decided I wanted to store ALL zip codes and their corresponding Cities in an external .csv, and store those values in arrays and run the code off that because if its worth doing, its worth overdoing! To clarify, this is no longer for homework, just to learn more about using external files in C#.
In the following code, I have called to open the file successfully, now I just need help in figuring out how to pull the data that is stored in two separate columns (one for city, one for zip code) and store them in two arrays to be acted upon by the for loop. Here is the code I have now. You can see how I have previously stored the other values in arrays and pulled them out:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnConvert2City_Click(object sender, EventArgs e)
{
try
{
string dir = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location);
string path = dir + #"\zip_code_database_edited.csv";
var open = new StreamReader(File.OpenRead(path));
int EnteredZipcode = Convert.ToInt32(txtZipcode.Text.Trim());
string result = "No Cities Found";
string[] Cities = new String[5] { "FLINTSTONE", "JAMAICA", "SCHENECTADY", "COTTONDALE", "CINCINNATI" };
int[] Zipcode = new int[5] { 30725, 11432, 12345, 35453, 45263 };
for (int i = 0; i <= Zipcode.Length - 1; i++)
{
if (Zipcode[i] == EnteredZipcode)
{
result = Cities[i];
break;
}
}
string DisplayState = result;
txtCity.Text = DisplayState;
}
catch (FormatException)
{
MessageBox.Show("Input must be numeric value.");
}
catch (OverflowException)
{
MessageBox.Show("Zipcode to long. Please Re-enter");
}
}
private void btnConvert2Zipcode_Click(object sender, EventArgs e)
{
string dir = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location);
string path = dir + #"\zip_code_database_edited.csv";
var open = new StreamReader(File.OpenRead(path));
String EnteredCity = txtCity.Text.ToUpper();
string result = "No Zipcode Found";
string[] Cities = new String[5] { "FLINTSTONE", "JAMAICA", "SCHENECTADY", "COTTONDALE", "CINCINNATI" };
int[] Zipcode = new int[5] { 30725, 11432, 12345, 35453, 45263 };
for (int i = 0; i <= Cities.Length - 1; i++)
{
if (Cities[i] == EnteredCity)
{
result = Convert.ToString(Zipcode[i]);
break;
}
}
string DisplayZip = result;
txtZipcode.Text = DisplayZip;
}
}
The following data is a snippet of what the data in my excel .csv looks like:
zip,primary_city
44273,Seville
44274,Sharon Center
44275,Spencer
44276,Sterling
44278,Tallmadge
44280,Valley City
44281,Wadsworth
44282,Wadsworth
44285,Wayland
And so on for about 46,000 rows.
How can I pull the zip and the primary_city into two separate arrays (I'm guessing with some ".Split "," "line) that my for-loop can operate on?
Also, if there are better ways to go about this, please let me know (but be sure to leave an explanation as I want to understand where you are coming from).
Don't create two separate array.Create a separate class for city
class City
{
public string Name{get;set;}
public int ZipCode{get;set;}
}
Now to read the data from that csv file
List<City> cities=File.ReadAllLines(path)
.Select(x=>new City
{
ZipCode=int.Parse(x.Split(',')[0]),
Name=x.Split(',')[1]
}).ToList<City>();
Or you can do this
List<City> cities=new List<City>();
foreach(String s in File.ReadAllLines(path))
{
City temp=new City();
temp.ZipCode=int.Parse(s.Split(',')[0]);
temp.Name=s.Split(',')[1];
cities.Add(temp);
}
You can try this:
string dir = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location);
string path = dir + #"\zip_code_database_edited.csv";
var open = new StreamReader(File.OpenRead(path));
var cities = new HashList<string>();
var zipCodes = new HashList<int>();
var zipAndCity = new string[2];
string line = string.Empty;
using (open)
{
while ((line = reader.ReadLine()) != null)
{
zipAndCity = line.Split(",");
zipCodes.Add(int.Parse(zipAndCity[0]));
cities.Add(zipAndCity[1]);
}
}
I am posting this answer having learned much more about C# since I posted this question. When reading a CSV, there are better options than String.Split().
The .NET Framework already has a built-in dedicated CSV parser called TextFieldParser.
It's located in the Microsoft.VisualBasic.FileIO namespace.
Not only are there many edge cases that String.Split() is not properly equipped to handle, but it's also much slower to use StreamReader.

Categories