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*
I am trying to create a multidimensional list filled with an employee and their information.
Ex: "Jane Smith" "Manager" "75,000" "Dallas"
the code I have right now is giving me an out of range exception.
This bigROW[i].Add(ownName); and bigROW[i][j+1] = newElement; gives me errors.
//Begin making rows
for (int i = 0; i < fileRowCount; i++ )
{
string findOwners = "";
findOwners = file5Data.Rows[i][0].ToString();
if(DISTINCTOppOwners.Contains(findOwners))
{
//Find index of where owner is
int useIndex = 0;
useIndex = DISTINCTOppOwners.IndexOf(findOwners);
//Add their name to Multidimensional list
string ownName = DISTINCTOppOwners[useIndex].ToString();
//This line give me the ERROR
bigROW[i].Add(ownName);
for (int j = 0; j < fileColCount; j++)
{
Add Employee information to Multidimensional list
string newElement = file5Data.Rows[i][j].ToString();
if(ownName != newElement)
{
if(j ==0)
{
//Avoid adding their names to the list twice
bigROW[i][j+1] = newElement;
}
bigROW[i][j] = newElement;
}
}
}
}
I tried adding the info to a list called "sublist" then adding it to the BigRow(multidimensional list),but when I cleared the sublist to add a new row it deleted the values from the BigRow.
I tried adding the info to a list called "sublist" then adding it to the BigRow(multidimensional list),but when I cleared the sublist to add a new row it deleted the values from the BigRow.
When you add an object to a list what is stored is a reference, not the contents of the object. Instead of clearing sublist you should create a new List each time. Otherwise you have an outer list that contains multiple copies of the same list inside.
Refer to jdweng's answer above for an example of this. In his code the ToList call creates an new numbers list for each line, so that each row has its own List of numbers.
Here is a simple example
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication64
{
class Program
{
static void Main(string[] args)
{
string input =
"0,1,2,3,4,5,6,7,8,9\n" +
"10,11,12,13,14,15,16,17,18,19\n" +
"20,21,22,23,24,25,26,27,28,29\n" +
"30,31,32,33,34,35,36,37,38,39\n" +
"40,41,42,43,44,45,46,47,48,49\n";
List<List<int>> output = new List<List<int>>();
StringReader reader = new StringReader(input);
string inputline = "";
while ((inputline = reader.ReadLine()) != null)
{
List<int> numbers = inputline.Split(new char[] { ',' }).Select(x => int.Parse(x)).ToList();
output.Add(numbers);
}
}
}
}
I am trying to read a .txt file using c# and displaying its contents but I am getting error as IndexOutOfRangeException with error code as 0xc000013a.
Here's my code:
static void Main(string[] args)
{
StreamReader sStreamReader = new StreamReader("d:\\TEST.txt");
while (!sStreamReader.EndOfStream)
{
string sLine = "";
if (sLine != null)
{
sLine = sStreamReader.ReadLine();
if (sLine != null)
{
string[] rows = sLine.Split(",".ToCharArray());
double a = Convert.ToDouble(rows[1]);
Console.Write(a);
int b = Convert.ToInt32(rows[3]);
Console.WriteLine(b);
Console.WriteLine();
}
}
}
}
my text file is as follows:
1,2,3,4,5,6,7
1,2,3,4,5,6,7
5,6,2,7,3,8,4
3,4,3,4,3
5,3,23,12
12,30000,12,99
I would change it to the following:
static void Main(string[] args)
{
// StreamReader is IDisposable which should be wrapped in a using statement
using (StreamReader reader = new StreamReader(#"d:\TEST.txt"))
{
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
// make sure we have something to work with
if (String.IsNullOrEmpty(line)) continue;
string[] cols = line.Split(',');
// make sure we have the minimum number of columns to process
if (cols.Length < 4) continue;
double a = Convert.ToDouble(cols[1]);
Console.Write(a);
int b = Convert.ToInt32(cols[3]);
Console.WriteLine(b);
Console.WriteLine();
}
}
}
Some notes here:
StreamReader implements IDisposable, so you should wrap it in a using clause so that it is properly disposed of.
Don't name things like "sLine". That form of Hungarian is commonly recognized as seriously bad practice. Even Microsoft says don't do it.
You're dealing with columns, not rows. So that variable should be named appropriately.
Always test to make sure you have all of the columns you need before blindly accessing them.
Normally, I wouldn't use Convert.ToDouble or Convert.ToInt32. It's much safer to use TryParse to make sure it was able to convert. The code you have will blow if cols[1] and cols[3] had non-numeric data.
You can use the # symbol in front of a string to tell the compiler that it doesn't need to be escaped.
It's much cleaner to simply "continue" a loop instead of wrapping it in a if statement.
Setting a String variable to a blank string then immediately setting it to some other value causes the blank to stay in memory for the entire scope. In other words, it's wasting memory. Granted, in this case it's a micro-optimization, but it never hurts to use best practices all of the time.
Have you considered checking for row.Length before accessing row[1] and row[3]
I suspect your empty lines are the problem
Here is how you can do it simpler:
string[] lines = File.ReadAllLines("d:\\TEST.txt");
foreach (var line in lines.Where(line => line.Length > 0))
{
string[] numbers = line.Split(',');
// It checks whether numbers.Length is greater than
// 3 because if maximum index used is 3 (numbers[3])
// than the array has to contain at least 4 elements
if (numbers.Length > 3)
{
double a = Convert.ToDouble(numbers[1]);
Console.Write(a);
int b = Convert.ToInt32(numbers[3]);
Console.Write(b);
Console.WriteLine();
}
}
You should consider to use :
if (!string.IsNullOrEmpty(sLine))
instead of
if (sLine != null)
You have this exceptions because some lines are empty.
However, here is a way you should write your code when using a StreamReader :
using(var reader = new StreamReader(#"d:\\TEST.txt"))
{
string line;
while ((line= reader.ReadLine()) != null)
{
if (string.IsNullOrEmpty(line)) continue;
var rows = line.Split(",".ToCharArray());
var a = Convert.ToDouble(rows[1]);
Console.Write(a);
var b = Convert.ToInt32(rows[3]);
Console.WriteLine(b);
Console.WriteLine();
}
}
Regards,
Kévin
Suppose I have a text file with data like below, I want to read first row and store the elements in one array. Read the second row and store in second array and so on. I will be making some manipulations on the array later on. Can you help me to do this in C#?
Input text file:
5,7,3,6,9,8,3,5,7
5,6,8,3,4,5
6,4,3,2,65,8,6,3,3,5,7,4
4,5,6,78,9,4,2,5,6
The Code I am trying out is:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace ReadFile
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
static void Main(string[] args)
{
TextReader tr = new StreamReader("Data.txt");
// write a line of text to the file
string word = tr.ReadLine();
//now split this line into words
string[] val = word.Split(new Char[] { ',' });
}
}
}
If I use the above technique, I can get the first line in array val. Is there a way to loop it for all the rows?
File.ReadAllLines will help you read all lines from file as string array.
String.Split will help you split line in pieces.
Int.Parse will help you transform string to int (double has similar method).
Ok, since you are a beginner my suggestions on what to focalize your attention.
First of all, are your consumer always needing all the lines or can it stop at some point? If so, use a yield strategy to return the results one by one. You can use a StreamReader to read line one by one in any case, and use double.TryParse(...) or int.TryParse() after splitting the string by the separator. Keep an eye to the fact that separator can change, so use it with some kind of configurability, and in the case of double, ensure your code works even in machines with a different decimal point configured. If you are sure your csv alwais uses '.' as a decimal point separator, specify
double.TryParse("",System.Globalization.CultureInfo.InvariantCulture);
Something roughly like this would work:
StreamReader reader = new (File.OpenRead(#"YourFile.txt"));
List<string> LstIntegers = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
//values is actually a string array.
var values = line.Split(',');
//add the entire array fetched into string List
LstIntegers.AddRange(values);
}
// important to close the reader. You can also use using statement for reader. It
// will close the reader automatically when reading finishes.
reader.Close();
// You can then further manipulate it like below, you can also use int.Parse or int.TryParse:
foreach (var v in LstIntegers)
{
// use int.TryParse if there is a chance that a non-int is read.
int someNum = Convert.ToInt32(v);
}
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.IO;
using System.Xml.Linq;
using System.Diagnostics;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
List<int[]> arrays = new List<int[]>();
int counter = 0;
string line;
// Read the file
System.IO.StreamReader file =
new System.IO.StreamReader("c:\\temp\\test.txt");
while ((line = file.ReadLine()) != null)
{
// split the line into a string array on , separator
string[] splitLine = line.ToString().Split(',');
// if our split isnt null and has a positive length
if (splitLine != null && splitLine.Length > 0)
{
// create a lineArray the same size as our new string[]
int[] lineArray = new int[splitLine.Length];
int posCounter = 0;
foreach (string splitValue in splitLine)
{
// loop through each value in the split, try and convert
// it into an int and push it into the array
try
{
lineArray[posCounter] = Int32.Parse(splitValue);
}
catch { }
posCounter++;
}
// if our lineArray has a positive length then at it to our
// list of arrays for processing later.
if (lineArray.Length > 0)
{
arrays.Add(lineArray);
}
}
counter++;
}
file.Close();
// go through the List<int[]> and print to screen
foreach (int[] row in arrays)
{
foreach (int rowCol in row)
{
Console.Write(rowCol + ",");
}
Console.WriteLine();
}
// Suspend the screen.
Console.ReadLine();
}
}
}
See this lines of code:
List<List<int>> numbers = new List<List<int>>();
foreach (string line in File.ReadAllLines(""))
{
var list = new List<int>();
foreach (string s in line.Split(new[]{',', ' '},
StringSplitOptions.RemoveEmptyEntries))
{
int i;
if(int.TryParse(s, out i))
{
list.Add(i);
}
}
numbers.Add(list);
}
var specialNumber = numbers[3][4]; // gives line 3 number 4
var specialLine = numbers[2].ToArray(); // gives an array of numbers of line 2
description:
I used this useful Generic:
List: Represents a strongly typed list of objects that can be accessed by index
and these useful Classes:
File.ReadAllLines: Opens a text file, reads all lines of the file into a string array
String.Split : Returns a string array that contains the substrings in this instance that are delimited by elements of a specified string or Unicode character array.
Int.TryParse: Converts the string representation of a number to its 32-bit signed integer equivalent.
I have presented the following code in two sections, just to show you in steps.
SECTION 1
This code is just to show that you just needed to loop through all lines and store string numbers from each line in a List.
SECTION 1
static void Main(string[] args)
{
List<string[]> allLines = new List<string[]>();
TextReader tr = new StreamReader("Data.txt");
string word = tr.ReadLine();
// write a line of text to the file
while ( word != null ) {
//now split this line into words
string[] vals = word.Split(new Char[] { ',' });
//Add this line into allLines
allLines.Add(vals);
//Now read the next line
word = tr.ReadLine();
}
}
SECTION 2
This section will get you the results as int.
static void Main(string[] args)
{
//A list of arrays of integers
//A single array will have numbers from a single line
List<int[]> allNumbers = new List<int[]>();
TextReader tr = new StreamReader("Data.txt");
string word = tr.ReadLine();
// write a line of text to the file
while ( word != null ) {
//now split this line into words
string[] vals = word.Split(new Char[] { ',' });
int[] intVals = new int[vals.Length];
for ( int i = 0; i < vals.Length; i++) {
Int32.TryParse(vals[i], out intVals[i]);
}
//Add this array of integers into allNumbers
allNumbers.Add(intVals);
//Now read the next line
word = tr.ReadLine();
}
}
NOTE: I have not compiled or tested the code above.
I figured it out. Thanks all for your time!
private void ReadFile()
{
var lines = File.ReadLines("Data.csv");
var numbers = new List<List<double>>();
var separators = new[] { ',', ' ' };
/*System.Threading.Tasks.*/
Parallel.ForEach(lines, line =>
{
var list = new List<double>();
foreach (var s in line.Split(separators, StringSplitOptions.RemoveEmptyEntries))
{
double i;
if (double.TryParse(s, out i))
{
list.Add(i);
}
}
lock (numbers)
{
numbers.Add(list);
}
});