I was trying to put loop through and put CSV to 2D array.
My app crashes due to var tokens = sr.ReadLine().Split(','); It throws a NullPointerException. How can I fix this?
Below is my whole method named csvToArray:
public string[,] csvToArray (string filePath)
{
int col = colCount(filePath);
int row = rowCount(filePath);
string line;
string[,] data = new string[col, row];
using (StreamReader sr = new StreamReader(filePath))
{
for (int i = 0; i < col; i++)
{
var tokens = sr.ReadLine().Split(',');
for (int j = 0; j < row; j++)
{
data[i, j] = tokens[j];
}
}
}
return data;
}
What does not make sense is that it finished the whole loop. The variables below the exception have the values that they were supposed to have.
You have to use first for loop for row and then inner loop for column.
public static string[,] csvToArray(string filePath)
{
int col = colCount(filePath);
int row = rowCount(filePath);
string line;
string[,] data = new string[row, col];
using (StreamReader sr = new StreamReader(filePath))
{
for (int i = 0; i < row; i++)
{
var tokens = sr.ReadLine().Split(',');
for (int j = 0; j < col; j++)
{
data[i, j] = tokens[j];
}
}
}
return data;
}
In your code you are doing null.Split(), that is why it giving you exception.
If you want to insert 0 in each cell for a blank row, then you can implement the following code.
for (int i = 0; i < row; i++)
{
string content = sr.ReadLine();
if (!string.IsNullOrEmpty(content))
{
var tokens = content.Split(',');
for (int j = 0; j < col; j++)
{
data[i, j] = tokens[j];
}
}
else
{
for (int j = 0; j < col; j++)
{
data[i, j] = "0";
}
}
}
Lets first analyse errors in your example.
1) Why do you event need to know length of row and columns in the beggining? It is overhead.
2) Row and columns in your loop is invalid.
3) This exception throws in your example because your reached EOF.
So, here is better way to read csv to 2D matrix:
public int[][] csvToArray (string filePath)
{
string line = null;
var result = new List<int[]>();
using (var sr = new StreamReader(filePath))
{
while((line = sr.ReadLine()) != null)
{
if(string.IsNullOrWhiteSpace(line)) continue;
result.Add(sr.Split(',').Select(x=> string.IsNullOrWhiteSpace(x) ? 0 : int.Parse(x)).ToArray());
}
}
return result.ToArray();
}
Then you can just check your matrix for consistency.
At least, this way you won't open your file three times and protected from counting errors.
Related
I'm currently trying to write a 2d int array to file. I've gotten it to write the data to file but it all appears on the same line.
Currently its appearing like this:
15101219816911171076171411881213567131441415101069101413878101181210156976139188711571210891267910
But I want it to appear like this:
15,10,12,19,8
16,9,11,17,10
7,6,17,14,11
8,8,12,13,5
6,7,13,14,4
1,4,15,10,10
6,9,10,14,13
8,7,9,10,11
8,12,10,15,6
9,7,6,13,9
18,8,7,11,5
7,12,10,8,9
12,6,7,9,10
This is my code.
{
StreamWriter outputfile;
File.WriteAllText("ClosingStock.Txt", string.Empty);
outputfile = File.AppendText("ClosingStock.Txt");
for (int i = 0; i < (StockColumns); i++)
{
for (int j = 0; j < (StockRows); j++)
{
outputfile.Write(Global_Stock[i , j]);
}
}
outputfile.Close();
}
}
Any help with this problem would be much appreciated. Thanks.
{
StreamWriter outputfile;
string comma = "";
File.WriteAllText("ClosingStock.Txt", string.Empty);
outputfile = File.AppendText("ClosingStock.Txt");
for (int i = 0; i < (StockColumns); i++)
{
for (int j = 0; j < (StockRows); j++)
{
outputfile.Write(comma);
outputfile.Write(Global_Stock[i , j]);
comma = ",";
}
comma = System.Environment.NewLine);
}
outputfile.Close();
}
You should first create a string to write into a file and then write it at once
StringBuilder sb = new StringBuilder();
for (int i = 0; i < (StockColumns); i++)
{
for (int j = 0; j < (StockRows); j++)
{
sb.Append(Global_Stock[i , j]);
sb.Append(",")
}
}
File.WriteAllText("ClosingStock.Txt",sb.ToString());
Just add a new line after each inner loop:
for (int i = 0; i < (StockColumns); i++)
{
for (int j = 0; j < (StockRows); j++)
{
outputfile.Write(Global_Stock[i , j]);
}
outputfile.Write(Environment.NewLine);
}
If I had to guess you should switch StockColumns with StockRows in your loops to have the output you want.
Also, see #Mihir Dave for better implementation of writing a string to a file.
I am trying to load data from a .txt file which looks like this:
|ABC|DEF|GHI|
|111|222|333|
|444|555|666|
With code:
using (StringReader reader = new StringReader(new StreamReader(fileStream, Encoding.Default).ReadToEnd()))
{
string line;
//reader.ReadLine(); //skip first line
while (reader.Peek() != -1)
{
line = reader.ReadLine();
if (line == null || line.Length == 0)
continue;
string[] values = line.Split('|').Skip(1).ToArray();
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
}
DataRow row = table.NewRow();
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
products++;
}
}
The problem is, when I generate a DataTable, I have first line as Column, but first line:
|ABC|DEF|GHI|
is visible also in the rows:
How to put first line as column headers and rest as rows?
I do not want to use CSVHelper for that if it possible.
Just need to skip when the first line after header is created
string line;
bool bheader= false;
//reader.ReadLine(); //skip first line
while (reader.Peek() != -1)
{
line = reader.ReadLine();
if (line == null || line.Length == 0)
continue;
string[] values = line.Split('|').Skip(1).ToArray();
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
bheader = true;
}
if(bheader ==false){
DataRow row = table.NewRow();
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
products++;
}
}
bheader = false;
}
The issue with your current code is that you handle when isColumnCreated is false, but not true. If you change this:
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
}
DataRow row = table.NewRow();
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
products++;
to this
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
}
DataRow row = table.NewRow();
else if (isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
}
it should work just fine. By only creating a row if the column headers have been created you're creating a situation wherein only on the first pass do you do anything with the first row, then it gets dumped.
I would think you want to add the columns or add a row.
if (!isColumnCreated)
{
for (int i = 0; i < values.Count(); i++)
{
table.Columns.Add(values[i]);
}
isColumnCreated = true;
}
}
else
{
DataRow row = table.NewRow();
for (int i = 0; i < values.Count(); i++)
{
row[i] = values[i];
}
table.Rows.Add(row);
}
This would work
DataTable dt = new DataTable();
using (System.IO.StreamReader sr = new System.IO.StreamReader("PathToFile"))
{
string currentline = string.Empty;
bool doneHeader = false;
while ((currentline = sr.ReadLine()) != null)
{
if (!doneHeader)
{
foreach (string item in currentline.Split('YourDelimiter'))
{
dt.Columns.Add(item);
}
doneHeader = true;
continue;
}
dt.Rows.Add();
int colCount = 0;
foreach (string item in currentline.Split('YourDelimiter'))
{
dt.Rows[dt.Rows.Count - 1][colCount] = item;
colCount++;
}
}
}
Another method, more LINQ oriented.
Use File.ReadAllLines to parse all the File lines into a string array.
Create a List<string[]> containing all the data Rows. The columns values are composed splitting the string row using a
provided Delimiter.
The first Row values are used to build a DataTable Columns elements.
The first Row is removed from the List.
All the other Rows are added to the DataTable.Rows collection.
Set a DataGridView.DataSource to the new DataTable.
char Delimiter = '|';
string[] Lines = File.ReadAllLines("[SomeFilePath]", Encoding.Default);
List<string[]> FileRows = Lines.Select(line =>
line.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries)).ToList();
DataTable dt = new DataTable();
dt.Columns.AddRange(FileRows[0].Select(col => new DataColumn() { ColumnName = col }).ToArray());
FileRows.RemoveAt(0);
FileRows.ForEach(row => dt.Rows.Add(row));
dataGridView1.DataSource = dt;
I am trying to get NPOI to work with ASP.NET (C#) and I want to read an excel file and put it in a DataSet. Here is the code I attempted:
public static DataTable getExcelData(string FileName, string strSheetName)
{
DataTable dt = new DataTable();
HSSFWorkbook hssfworkbook;
using (FileStream file = new FileStream(FileName, FileMode.Open, FileAccess.Read))
{
hssfworkbook = new HSSFWorkbook(file);
}
ISheet sheet = hssfworkbook.GetSheet(strSheetName);
System.Collections.IEnumerator rows = sheet.GetRowEnumerator();
while (rows.MoveNext())
{
IRow row = (HSSFRow)rows.Current;
if (dt.Columns.Count == 0)
{
for (int j = 0; j < row.LastCellNum; j++)
{
dt.Columns.Add(row.GetCell(j).ToString());
}
continue;
}
DataRow dr = dt.NewRow();
for (int i = 0; i < row.LastCellNum; i++)
{
ICell cell = row.GetCell(i);
if (cell == null)
{
dr[i] = null;
}
else
{
dr[i] = cell.ToString();
}
}
dt.Rows.Add(dr);
}
return dt;
}
The Error that I get is
+ $exception {"Object reference not set to an instance of an object."} System.Exception {System.NullReferenceException}
The odd thing is that this actually works with 2 excel files that I have, but when I put in a third one it crashes with that error.
This returns null if strSheetName isn't found:
ISheet sheet = hssfworkbook.GetSheet(strSheetName);
try:
for( int iSheet = 0; iSheet < hssfworkbook.NumberOfSheets; ++iSheet )
{
ISheet sheet = hssfworkbook.GetSheetAt(iSheet); // could cast to HSSFSheet
String strSheetNameActual = sheet.SheetName;
}
Then figure out how you want to compare strSheetName to strSheetNameActual or which sheets you want to process and how.
Try using this:
for (int j = row.FirstCellNum; j < row.LastCellNum; j++)
and
for (int i = row.FirstCellNum; i < row.LastCellNum; i++)
Instead of:
for (int j = 0; j < row.LastCellNum; j++)
and
for (int i = 0; i < row.LastCellNum; i++)
Also, make sure that you manage the case when the cells on the first row are null:
if (dt.Columns.Count == 0)
{
int empty = 0;
for (int j = row.FirstCellNum; j < row.LastCellNum; j++)
{
ICell cell = row.GetCell(j);
if (cell == null)
{
dt.Columns.Add(String.Format("emptyColumnName_{0}", empty++));
}
else
{
dt.Columns.Add(row.GetCell(j).ToString());
}
}
continue;
}
If you always want to read from the first sheet (probably, to get rid of the second method parameter, the sheet name, which is also the cause of your error), you may use:
// rest of the method's code
ISheet sheet = hssfworkbook.GetSheetAt(0);
if (sheet == null)
return dt;
var rows = sheet.GetRowEnumerator();
// rest of the method's code
I'm working on building a little game, and I'm having an issue with reading a level map from a .txt file, converting it to a 2D array of characters, then converting any non blank character to an object of one type or another. I think converting to the 2D array is fine, but it looks like there's a null reference exception in one of two for loops traversing the array when converting to objects. Here's the code:
public void buildLevel()
{
List<Tangible> objects = new List<Tangible>(0);
for (int i = 0; i < map.GetLength(1); i++)
{
for (int j = 0; j < map.GetLength(0); j++)
{
if (map[j, i] == 'O')
{
objects.Add(new Ball((50 * j), (50 * i)));
}
if (map[j, i] == 'X')
{
objects.Add(new Block((50 * j), (50 * i), 50, 50));
}
}
}
allObjects = objects;
}
Any idea what might be happening here? I really appreciate it.
EDIT: here's the code for reading the file into a 2D array:
public void setMap(String mapFile)
{
try
{
List<String> lines = new List<String>();
using (StreamReader sr = new StreamReader("maps\\testLevel.txt"))
{
String currentLine;
while ((currentLine = sr.ReadLine()) != null)
{
lines.Add(currentLine);
}
}
map = new char[lines[0].Length, lines.Count];
for (int i = 0; i < lines.Count; i++)
{
for (int j = 0; j < lines[0].Length; j++)
{
map[j, i] = lines[i][j];
}
}
}
catch (DirectoryNotFoundException e)
{
Console.Error.WriteLine(e);
}
}
Okay so I've managed to read in a .txt file... now I'm trying to figure the best way to convert this information into a 2D array.
My text file (first two number provide height and width):
5
5
0,0,0,0,0
0,0,0,0,0
0,0,1,0,0
0,1,1,1,0
1,1,1,1,1
My C# / XNA:
string fileContents = string.Empty;
try
{
using (StreamReader reader = new StreamReader("Content/map.txt"))
{
fileContents = reader.ReadToEnd().ToString();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Now what I need to do next is define the size of the 2-dimensional map array and then populate the entry values... this is where I'm getting a bit stuck and have found various ways I can loop through the data but I don't think any of them have been terribly tidy.
What I've tried to do is have one loops which splits by newline... and then another loop which splits by comma delimiter.
Is this the best way to do it... or are there better alternatives?
It can be done with LINQ but that is only practical when you want (accept) an array-of-array, int[][] instead of a straight 2-dimensional int[,] .
int[][] data =
File.ReadLines(fileName)
.Skip(2)
.Select(l => l.Split(',').Select(n => int.Parse(n)).ToArray())
.ToArray();
The code below doesn't require the first to rows in your sample .CSV file:
5
5
I'd prefer it this way, but as a consequence, the code below reads the file twice. It would take a small modification use the first two rows in your sample instead.
private int[,] LoadData(string inputFilePath)
{
int[,] data = null;
if (File.Exists(inputFilePath))
{
Dictionary<string, int> counts = GetRowAndColumnCounts(inputFilePath);
int rowCount = counts["row_count"];
int columnCount = counts["column_count"];
data = new int[rowCount, columnCount];
using (StreamReader sr = File.OpenText(inputFilePath))
{
string s = "";
string[] split = null;
for (int i = 0; (s = sr.ReadLine()) != null; i++)
{
split = s.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
for (int j = 0; j < columnCount; j++)
{
data[i, j] = int.Parse(split[j]);
}
}
}
}
else
{
throw new FileDoesNotExistException("Input file does not exist");
}
return data;
}
private Dictionary<string, int> GetRowAndColumnCounts(string inputFilePath)
{
int rowCount = 0;
int columnCount = 0;
if (File.Exists(inputFilePath))
{
using (StreamReader sr = File.OpenText(inputFilePath))
{
string[] split = null;
int lineCount = 0;
for (string s = sr.ReadLine(); s != null; s = sr.ReadLine())
{
split = s.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (columnCount == 0)
{
columnCount = split.Length;
}
lineCount++;
}
rowCount = lineCount;
}
if (rowCount == 0 || columnCount == 0)
{
throw new FileEmptyException("No input data");
}
}
else
{
throw new FileDoesNotExistException("Input file does not exist");
}
Dictionary<string, int> counts = new Dictionary<string, int>();
counts.Add("row_count", rowCount);
counts.Add("column_count", columnCount);
return counts;
}
Here's the solution I've come up with which appears to work.
int[,] txtmap;
int height = 0;
int width = 0;
string fileContents = string.Empty;
try
{
using (StreamReader reader = new StreamReader("Content/map.txt"))
{
fileContents = reader.ReadToEnd().ToString();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
string[] parts = fileContents.Split(new string[] { "\r\n" }, StringSplitOptions.None);
for (int i = 0; i < parts.Length; i++)
{
if (i == 0)
{
// set width
width = Int16.Parse(parts[i]);
}
else if (i == 1)
{
// set height
height = Int16.Parse(parts[i]);
txtmap = new int[width, height];
}
if (i > 1)
{
// loop through tiles and assign them as needed
string[] tiles = parts[i].Split(new string[] { "," }, StringSplitOptions.None);
for (int j = 0; j < tiles.Length; j++)
{
txtmap[i - 2, j] = Int16.Parse(tiles[j]);
}
}
}