C# - Stream Reader or whats the best way to do it? - c#

I have a text file with something like this in it.
Tom 1 2
Jerry 3 4
using C#, I have populate this into two arrays
1st array = {Tom,Jerry} - 1 dim array
2nd array ={(1,2),(3,4)} - 2 dim array
Please help me with this. Any help would be appreciated.
Console.WriteLine("Enter the file name with extension:");
string filename = Console.ReadLine();
string s = System.IO.File.ReadAllText("C:/Desktop/" + filename);
Console.WriteLine("\n Text Details in the file: \n \n"+s);

I guess this is a follow up to the last question :)
More hw hints:
As I said in my last answer, splitting on tab (assuming each item is delimited by tab, which looks to be the case) will give you a 1D array of every item in a line (if you use ReadLine).
Item 1 in the ReadLine() array will be the name. Put that into your 1D names array.
Items 2 to N of ReadLine() array will be the test scores. Put that into your 2D scores array.
The first dimension of the scores array will be the student index. The second dimension will be the score array.
That may sound confusing, but if you think about it, a 2D array is an array of arrays.
So even though your data file doesn't show the student index, it's implied:
0 Joe 100 80 77
1 Bob 65 93 100
Names array will look like:
[0] Joe
[1] Bob
and scores array will look like:
[0][0] 100
[0][1] 80
[0][2] 77
[1][0] 65
[1][1] 93
[1][2] 100
Notice that the index (first dimension) in the scores array coincide with the index of the names array.

There are few ways, it depends how "elegant" you want to be, and / or whether Tom, Jerry is always going to be one word.
Parse every line with String methods
Parse every line with RegEx
Use Linq to Text
Simplest way would be something like this (quick and dirty, very fragile solution):
var path = "fileName.txt";
var names = new List<string>();
var values = new List<KeyValuePair<int, int>>();
using (var reader = File.OpenText(path))
{
string s = "";
while ((s = reader.ReadLine()) != null)
{
String[] arr = s.Split(' ');
names.Add(arr[0]);
values.Add(new KeyValuePair<int, int>(int.Parse(arr[1]), int.Parse(arr[2])));
}
}
If you need you can convert lists to array

A more complete version ;)
string filename = "";
do
{
Console.WriteLine("Enter the file name with extension:");
filename = Environment.GetEnvironmentVariable("HOMEDRIVE") + Environment.GetEnvironmentVariable("HOMEPATH") + "\\Desktop\\" + Console.ReadLine();
if (!System.IO.File.Exists(filename))
Console.WriteLine("File doesn't exist!");
else
break;
} while (true);
System.IO.StreamReader readfile = new System.IO.StreamReader(filename);
List<string> Names = new List<string>();
List<int[]> Numbers = new List<int[]>();
string val = "";
while ((val = readfile.ReadLine()) != null)
{
if (val == string.Empty)
continue;
List<string> parts = val.Split(' ').ToList<string>();
Names.Add(parts[0]);
parts.RemoveAt(0);
Numbers.Add(parts.ConvertAll<int>(delegate(string i) { return int.Parse(i); }).ToArray());
}
readfile.Close();
//Print out info
foreach (string name in Names)
{
Console.Write(name + ", ");
}
Console.WriteLine();
foreach (int[] Numberset in Numbers)
{
Console.Write("{");
foreach (int number in Numberset)
Console.Write(number + ", ");
Console.Write("} ");
}
Console.ReadLine();

I like a functional approach.
// var fileContent = System.IO.File.ReadAllText("somefilethathasthestuff");
var fileContent = #"Tom 1 2
Jerry 3 4";
var readData = fileContent.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
.Aggregate(new { names = new List<string>(), data = new List<int[]>() },
(result, line) => {
var fields = line.Split(new []{' '}, 2);
result.names.Add(fields[0]);
result.data.Add(fields[1].Split(new[] { ' ' }).Select(n => int.Parse(n)).ToArray());
return result;
}
);
string[] firstarray = readData.names.ToArray();
int[][] secondarray = readData.data.ToArray();
This uses a jagged array for the numbers, but you can copy it to a 2d if that is what you really need. Better yet, don't copy to arrays at all. Use List < string> for names and List < int[] > for the numbers.

Related

split multiple whitespaces from text file to array

I have a text file and required to have all 7 elements including the empty ones to be parsed into an array for further processing. However, there are no unique delimiter to be make use of except for whitespaces and some of the data/value will come with whitespace. Example per "Data Sample" and some of the block will have null entry. How can i make this happen?
Snippet of Data
Actual Sample Data
My end results would be some ting similar like below:
Array[0]:123456789
Array[1]:HLTX
Array[2]:5
Array[3]:BT5Q02
Array[4]:4SV
Array[5]:D8041
Array[6]:LIANG LIN
My code for the above function for now per below and it will omit the empty values. Which likely will missed out some of the data required.
string[] splitlinecontent = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var OrderNum = splitlinecontent[0];
var OrderType = splitlinecontent[1];
int OrderQTY = int.Parse(splitlinecontent[2]);
var OrderSINumInRpt = splitlinecontent[3];
var OrderHoldMod = splitlinecontent[5];
var SalesPerson = splitlinecontent[6];
I think bestpractice for these files is to use TextFieldParser from Microsoft.VisualBasic.FileIO;
using (var parser = new TextFieldParser(fileName))
{
parser.TextFieldType = FieldType.FixedWidth;
parser.SetFieldWidths(3, 7, 10, 13, 8, 6, 1, 7, -1);
while (!parser.EndOfData)
{
var fields = parser.ReadFields();
But I guess it isn't that hard to code the stuff yourself.
Based on the screenshot of your sample data, your columns have a fixed charsize of ten chars. You now can simply read the sample data line by line and split the lines by this fixed size.
public static List<List<string>> GetRecords(string path, bool hasColHeader, int colLength, int colCount){
//Result will be stored in lists
List<List<string>> result = new List<List<string>>();
//Get the sample file
string[] records = File.ReadAllLines(path,Encoding.UTF8);
//Go for each line through the data from sample file
for(int n = 0; n<records.Length;n++){
//create new list for this line
result.Add(new List<string>());
//here you can do something with headers. for simplification i do nothing with them and continue with next line.
if(n==0 && hasColHeader){
continue;
}
//go for each column (colCount specifies the count of columns)
for(int i = 0; i< colCount ;i++){
//if the length of the line is not devisible by colLength, you have to put some spaces to match the columns size
//not the best way to do this but this is not the major point of this question
if(records[n].Length % colLength != 0){
int charsToAdd = (colLength * colCount) - records[n].Length;
string spaces = "";
for(int s = 0; s< charsToAdd; s++){
spaces += " ";
}
records[n] += spaces;
}
//add the result to the currently created list
result[n].Add(records[n].Substring(i*colLength,colLength).Trim());
}
}
return result;
}
You can use this code like this:
static void Main(string[] args)
{
List<List<String>> list = GetRecords(#"C:\temp\DataSample.txt",true, 10, 7);
}
The data in list looks like this:
List[0]:List[0]:123456789
List[0]:List[1]:HLTX
List[0]:List[2]:5
List[0]:List[3]:BT5Q02
List[0]:List[4]:4SV
List[0]:List[5]:D8041
List[0]:List[6]:LIANG LIN
List[1]:List[0]:3835443
List[1]:List[1]:HLTX
List[1]:List[2]:1
...
Here you can optimize two things by youreself.
Calculate the size of the columns by the chars between headers. The columnsize will alsways be the start of a columnheader and the start of the next columnheader. The charcount between this two points, will be the size of the column.
Find a better way to get the last column! :D i dont think what i've done is good. There a better ways to do this.

C# Populate An Array with Values in a Loop

I have a C# console application where an external text file is read. Each line of the file has values separated by spaces, such as:
1 -88 30.1
2 -89 30.1
So line one should be split into '1', '-88', and '30.1'.
What I need to do is to populate an array (or any other better object) so that it duplicate each line; the array should have 3 elements per row. I must be having a brain-lock to not figure it out today. Here's my code:
string line;
int[] intArray;
intArray = new int[3];
int i = 0;
//Read Input file
using (StreamReader file = new StreamReader("Score_4.dat"))
{
while ((line = file.ReadLine()) != null && line.Length > 10)
{
line.Trim();
string[] parts;
parts = line.Split(' ');
intArray[0][i] = parts[0];//error: cannot apply indexing
i++;
}
}
Down the road in my code, I intend to make some API calls to a server by constructing a Json object while looping through the array (or alternate object).
Any idea?
Thanks
If you only need the data to be transferred to JSON then you don't need to process the values of the data, just reformat it to JSON arrays.
As you don't know the number of lines in the input file, it is easier to use a List<>, whose capacity expands automatically, to hold the data rather than an array, whose size you would need to know in advance.
I took your sample data and repeated it a few times into a text file and used this program:
static void Main(string[] args)
{
string src = #"C:\temp\Score_4.dat";
List<string> dataFromFile = new List<string>();
using (var sr = new StreamReader(src))
{
while (!sr.EndOfStream)
{
string thisLine = sr.ReadLine();
string[] parts = thisLine.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 3)
{
string jsonArray = "[" + string.Join(",", parts) + "]";
dataFromFile.Add(jsonArray);
}
else
{
/* the line did not have three entries */
/* Maybe keep a count of the lines processed to give an error message to the user */
}
}
}
/* Do something with the data... */
int totalEntries = dataFromFile.Count();
int maxBatchSize = 50;
int nBatches = (int)Math.Ceiling((double)totalEntries / maxBatchSize);
for(int i=0;i<nBatches;i+=1)
{
string thisBatchJsonArray = "{\"myData\":[" + string.Join(",", dataFromFile.Skip(i * maxBatchSize).Take(maxBatchSize)) + "]}";
Console.WriteLine(thisBatchJsonArray);
}
Console.ReadLine();
}
to get this output:
{"myData":[[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1]]}
{"myData":[[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1],[1,-88,30.1],[2,-89,30.1]]}
It should be easy to adjust the format as required.
I would create a custom Item class and then populate a list, for easy access and sorting, with self contained items. something like:
public Class MyItem
{
public int first { get; set; }
public int second { get; set; }
public float third { get; set; }
public MyItem(int one, int two, float three)
{
this.first = one;
this.second = two;
this.third = three;
}
}
then you could do:
List<MyItem> mylist = new List<MyItem>();
and then in your loop:
using (StreamReader file = new StreamReader("Score_4.dat"))
{
while ((line = file.ReadLine()) != null && line.Length > 10)
{
line.Trim();
string[] parts;
parts = line.Split(' ');
MyItem item = new Item(Int32.Parse(parts[0]),Int32.Parse(parts[1]),Float.Parse(parts[2]));
mylist.Add(item);
i++;
}
}
As there are numbers like 30.1 so int is not suitable for this, and also it must not be a double[] but double[][]:
string[] lines = File.ReadAllLines("file.txt");
double[][] array = lines.Select(x => s.Split(' ').Select(a => double.Parse(a)).ToArray()).ToArray();
Issue is that int array is single dimensional.
My suggestion is that you can put a class with 3 properties and populate a list of class there. It's better to have class with same property names that you require to build JSON. So that you can easily serialize this class to JSON using some nugets like Newtonsoft and make api calls easily.
Your int array is a single dimensional array yet you're trying to index it like a multidemensional array. It should be something like this:
intArray[i] = parts[0]
(However you'll need to handle converting to int for parts that are fractional)
Alternatively, if you want to use a multidimensional array, you have to declare one.
int[][] intArray = new int[*whatever your expected number of records are*][3]
Arrays have a static size. Since you're reading from a file and may not know how many records there are until your file finishes reading, I recommend using something like a List of Tuples or a Dictionary depending on your needs.
A dictionary will allow you to have quick lookup of your records without iterating over them by using a key value pair, so if you wanted your records to match up with their line numbers, you could do something like this:
Dictionary<int, int[]> test = new Dictionary<int, int[]>();
int lineCount = 1;
while ((line = file.ReadLine()) != null && line.Length > 10)
{
int[] intArray = new int[3];
line.Trim();
string[] parts = line.Split(' ');
for (int i = 0; i < 3; i++)
{
intArray[i] = int.Parse(parts[i]);
}
test[lineCount] = intArray;
lineCount++;
}
This will let you access your values by line count like so:
test[3] = *third line of file*

Replacing data from a list with its position

I have made some code that allows the user to enter a sentence into a richtextbox, and then the data will be saved into a list and have the duplicates removed.
What I want to know is how I would make the overwrite the words in the list with their positions, and then replace the positions of the original sentence with those positions.
E.g: in the sentence Hello this is a test I hope this test works the sentence will be saved, removed of duplicates, and output hello, this, is, a, test, I, hope, works, the code replaces this with 1 2 3 4 5 6 7 8 (I think).
Now I need to make the program replace the actual words in the list with its position in the original so it will finally say 1 2 3 4 5 6 7 2 5 8 separated by commas.
This is my code:
string sentence = richTextBox1.Text;
list = sentence.Split(delimiterChars).ToList();
listoriginal = sentence.Split(delimiterChars).ToList();
listBox1.Items.Add("Full sentence: " + String.Join(" ", list));
list = list.Distinct(StringComparer.InvariantCultureIgnoreCase).ToList();
listBox1.Items.Add("Words in the input: " + String.Join(", ", list));
for (int i = 0; i < list.Count; i++)
{
list[i] = list[i].ToString();
listoriginal[i] = listoriginal[i].ToString();
resultList = listoriginal.Select(x => x.Replace(listoriginal[i], list[i])).ToList();
i++;
}
listBox1.Items.Add("Final result: " + String.Join(", ", resultList));
Create a dictionary with the words that have already been addressed. Meaning that you would have the following:
Dictionary<int, string> words = new Dictionary<int, string>();
words.Items.Add(//number, //word);
(Hello, 1)
(this, 2)
(is, 3)
(a, 4)
.....and so on
You can then write a a method that will search to see if a word is already stored, for example:
foreach(KeyValuePair<int, string> set in words)
{
if(set.value == //whatever word is next)
{
//write the number in the dictionary here
//the corresponding number can be grabbed using: set.key
}
}
Basically you need to populate on the fly a Dictionary<string, int> holding the result position of the word. The result words then can be obtained from Keys property:
var originalWords = sentence.Split(delimiterChars, StringSplitOptions.RemoveEmptyEntries);
var uniqueWordPositions = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
var originalWordPositions = new List<int>();
foreach (var word in originalWords)
{
int position;
if (!uniqueWordPositions.TryGetValue(word, out position))
uniqueWordPositions.Add(word, position = uniqueWordPositions.Count + 1);
originalWordPositions.Add(position);
};
listBox1.Items.Add("Full sentence: " + string.Join(" ", originalWords));
listBox1.Items.Add("Words in the input: " + string.Join(", ", uniqueWordPositions.Keys));
listBox1.Items.Add("Final result: " + string.Join(", ", originalWordPositions));
The easiest way is probably to use a key/value store like a dictionary - once you have eliminated the duplicates you can push the key/values into the dictionary and use that to re-build the sentence.
// Create dictionary
var dict = new Dictionary<string, int>();
// When looping through the words to determine index add each word to the dict - the word being the key and the value being the index
dict.Add(word, index);
Then
foreach(var word in words) {
// Get the value from the dictionary for the associated key (the key being the word)
var index = dict[word];
// do stuff with index
}
You can use string concatenation to re-build the string rather than replace (since you are indexing all words).
Common advice will be to use StringBuilder when you are concerned about performance/memory since strings are immutable
var sb = new StringBuilder();
foreach(var word in words) {
sb.Append(dict[word]);
sb.Append(" ");
}
sb.ToString();
Edit:
Here's a more full example...
var sentence = "hello world this is a test hello world";
var words = sentence.Split(' ');
var distinctWords = words.Distinct(StringComparer.InvariantCultureIgnoreCase);
var dict = new Dictionary<string, int>();
var ix = 0;
foreach (var word in distinctWords)
{
dict.Add(word.ToLower(), ix++);
}
var sb = new StringBuilder();
foreach (var word in words)
{
sb.Append(dict[word.ToLower()]);
sb.Append(" ");
}
// sb.ToString();
// 0 1 2 3 4 5 0 1
Obviously since I'm not doing a replace, this may affect the formatting of the original string but it gives you an idea. You can use replace but it will be a lot slower - but it depends on the length of string and how much processing you are doing.

Get first word on every new line in a long string?

I am trying to add a leaderboard in my unity app
I have a long string as below(just an example, actual string is http pipe data from my web service, not manually stored):
string str ="name1|10|junk data.....\n
name2|9|junk data.....\n
name3|8|junk data.....\n
name4|7|junk data....."
I want to get the first word (string before the first pipe '|' like name1,name2...) from every line and store it in an array and then get the numbers (10,9,8... arter the '|') and store it in an other one.
Anyone know whats the best way to do this?
Fiddle here: https://dotnetfiddle.net/utp4HK
code below, you may want to revisit the algorithm for performance, but if that is not an issue, this will do the trick;
using System;
public class Program
{
public static void Main()
{
string str ="name1|10|junk data.....\nname2|9|junk data.....\nname3|8|junkdata.....\nname4|7|junk data.....";
foreach (var line in str.Split('\n'))
{
Console.WriteLine(line.Split('|')[0]);
}
}
}
First split by new-line characters:
string[] lines = str.Split(new string[]{Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
Then you can use LINQ to get both arrays:
var data = lines.Select(l => l.Trim().Split('|')).Where(arr => arr.Length > 1);
string[] names = data.Select(arr => arr[0].Trim()).ToArray();
string[] numbers = data.Select(arr => arr[1].Trim()).ToArray();
Check out this link on splitting strings: http://msdn.microsoft.com/en-us/library/ms228388.aspx
You could first create an array of strings (one for each line) by splitting the long string with \n as the delimeter.
Then, you could split each line with | as the delimeter. The name would be the 0th index of the array and the number would be the 1st index of the array.
First of all, you can't have a multi line string without using verbatim string literal. With using verbatim string literal, you can split your string based on \r\n or Environment.NewLine like;
string str = #"name1|10|junk data.....
name2|9|junk data.....
name3|8|junk data.....
name4|7|junk data.....";
var array = str.Split(new []{Environment.NewLine},
StringSplitOptions.RemoveEmptyEntries);
foreach (var item in array)
{
Console.WriteLine(item.Split(new[]{"|"},
StringSplitOptions.RemoveEmptyEntries)[0].Trim());
}
Output will be;
name1
name2
name3
name4
Try this:
string str ="name1|10|junk data.....\n" +
"name2|9|junk data.....\n" +
"name3|8|junk data.....\n" +
"name4|7|junk data.....";
string[] tempArray1 = str.Split('\n');
string[] tempArray2 = null;
string[,] newArray = null;
for (int i = 0; i < tempArray1.Length; i++)
{
tempArray2 = tempArray1[i].Split('|');
if (newArray[0, 0].ToString().Length == 0)
{
newArray = new string[tempArray1.Length, tempArray2.Length];
}
for (int j = 0; j < tempArray2.Length; j++)
{
newArray[i,j] = tempArray2[j];
}
}

Need to add each number 0-x to end of line?

I'm making an app and I'm almost done. I just need to know how I can streamread a txt list and foreach line, add numbers 0-x (x will be the number the user puts in the textbox) and add it to a list. So basically, it would be like this
You import a list with 'dog' on one line, 'cat' on another, and 'fish' on the third. You type '5' into the textbox. the app puts all this into a list:
dog1
dog2
dog3
dog4
dog5
cat1
cat2
cat3
cat4
cat5
fish1
fish2
fish3
fish4
fish5
thanks!
The code below should work for you. I assume you can acquire the count value on your own.
var animals = File.ReadAllLines("yourFile.txt"); //new[] {"dog", "cat", "fish"};
var count = 5;
var merged =
from a in animals
from n in Enumerable.Range(1, count)
select a + n;
foreach (var m in merged)
Console.WriteLine(m); //act on each however you want
You can read a text file with File.ReadAllLines. This gives you an array you can iterate over with foreach.
In this foreach loop you can perform another loop from 1 to the number the user entered. int.Parse comes in handy for converting the string the user entered into a number C# can do something with. For the actual iteration you can use a for loop.
You can then add each item to a list.
There is a good example for reading each line in a filestream here: http://msdn.microsoft.com/en-us/library/e4y2dch9.aspx
private List<string> GetThings(string fileName, int count)
{
string[] lines = File.ReadAllLines(fileName);
List<string> result = new List<string>();
foreach (string item in lines)
{
for (int i = 1; i <= count; i++)
result.Add(item + i.ToString());
}
return result;
}
string[] inputList = File.ReadAllLines("yourFile.txt");
List<String> listOfThings = new List<String>();
foreach (string i in inputList)
{
for (int k = 0; k < 5; k++)
{
listOfThings.Add(i + " " + k.ToString());
}
}
then after that, you can print out the list like this:
foreach (string outp in listOfThings)
{
Console.WriteLine(outp);
}
output:
some value 0
some value 1
some value 2
some value 3
some value 4
some other value 0
some other value 1
some other value 2
some other value 3
some other value 4

Categories