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]);
}
}
}
Related
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.
I am writing a program in C# to read from a file and output to a csv file all of the unique words and the number of occurrences in the file for each word. My issue is when I try to run my program, I never get out of my while loop that goes line by line.
public override List<WordEntry> GetWordCount()
{
List<WordEntry> words = new List<WordEntry>();
WordEntry wordEntry = new WordEntry();
//string[] tokens = null;
string line, temp, getword;
int count = 0, index = 0;
long number;
while ((line = input.ReadLine()) != null)
{
if (line == null)
Debug.Write("shouldnt happen");
char[] delimit = { ' ', ',' };
string[] tokens = line.Split(delimit);
if (words.Count == 0)
{
wordEntry.Word = tokens[0];
wordEntry.WordCount = 1;
words.Add(wordEntry);
}//end if
for (int i = 0; i < tokens.Length; i++)
{
for (int j = 0; j < words.Count; j++)
{
if (tokens[i] == words[j].Word)
{
number = words[j].WordCount;
number++;
getword = words[j].Word;
wordEntry.WordCount = number;
wordEntry.Word = getword;
words.RemoveAt(j);
words.Insert(j, wordEntry);
}//end if
else
{
wordEntry.Word = tokens[i];
wordEntry.WordCount = 1;
words.Add(wordEntry);
}//end else
}//end for
}//end for
}//end while
return words;
}
It is getting stuck in the while loop as if it never reaches the end of the file. The file is 2.6 MB so it should be able to make it to the end.
Here's how you can rewrite your code to use a dictionary.
var words = new Dictionary<string,int>();
while ((line = input.ReadLine()) != null)
{
if (line == null)
Debug.Write("shouldnt happen");
char[] delimit = { ' ', ',' };
string[] tokens = line.Split(delimit);
foreach (var word in tokens)
{
if(words.ContainsKey(word))
words[word]++;
else
words.Add(word, 1);
}
}
This reduces the complexity of the code because dictionary has a O(1) lookup.
EDIT
You can convert the dictionary into List<WordEntry> like this.
return words
.Select(kvp => new WorkEntry
{
Word = kvp.Key,
WordCount = kvp.Value
})
.ToList();
I guess in fact your code doesn't get out of the "for (int j = 0; j < words.Count; j++)" because new items are kept being added to the words list.
I have been unable to apply any solutions to this issue. The exception happens to this line here: currentMap[row, col] = Int32.Parse(s); What I am wanting to do is pass this method a specific file storing rows of numbers like this:
1,1,1
1,0,1
1,1,1
I then want each number to be stored in int[,] currentMap which gets returned. The file I am using contains no large numbers. I think that the size of array I am creating is right and so I don't understand why this isn't working. I am used to doing similar stuff using NextInt in java but I couldn't find any alternative for c#.
Thanks for any help.
private int[,] LoadMapArray(String filename)
{
int[,] currentMap;
int rows = 0;
int cols = 0;
StreamReader sizeReader = new StreamReader(filename);
using (var reader = File.OpenText(filename))
{
while (reader.ReadLine() != null)
{
string line = sizeReader.ReadLine();
cols = line.Length;
rows++;
}
}
currentMap = new int[rows,cols];
StreamReader sr = new StreamReader(filename);
for (int row = 0; row < rows + 1; row++)
{
string line = sr.ReadLine();
string[] split = new string[] {","};
string[] result;
result = line.Split(split, StringSplitOptions.None);
int col = 0;
foreach (string s in result)
{
currentMap[row, col] = Int32.Parse(s);
col++;
}
}
return currentMap;
}
Edit: Code was fixed after changing how I was accessing the file. I then had to change this to catch null:
for (int row = 0; row < rows + 1; row++)
{
string line = sr.ReadLine();
string[] split = new string[] { "," };
string[] result;
if (line != null)
{
result = line.Split(split, StringSplitOptions.None);
int col = 0;
foreach (string s in result)
{
currentMap[row, col] = Int32.Parse(s);
col++;
}
}
}
No, the size of your array is not correct. You read two lines at each loop but you increment the rows counter just one time.
using (var reader = File.OpenText(filename))
{
string line = string.Empty;
while ((line = reader.ReadLine()) != null)
{
rows++;
}
}
And I am sure that also the cols count is not correct, but it doesn't raise an exception because you are dimensioning the cols dimension bigger than required. (You count also the space for the commas, not just the numbers)
A simpler approach (if your file is not very big) is to use File.ReadAllLines()
string[] split = new string[] {","};
string[] lines = File.ReadAllLines(filename);
int rows = lines.Length;
int cols = lines[0].Split(split, StringSplitOptions.RemoveEmptyEntries).Count();
currentMap = new int[rows,cols];
for (int row = 0; row < rows; row++)
{
string line = lines(row);
string[] result = line.Split(split, StringSplitOptions.None);
int col = 0;
foreach (string s in result)
{
int value;
Int32.TryParse(s, out value)
currentMap[row, col] = value;
col++;
}
}
Now, the entire file is in memory with just one disk operation and you could work using the in memory strings. The parsing of the integer should be changed to use Int32.TryParse to avoid exception in case the retrieved value is not a valid integer.
Can you please help me with C#.
I am trying to create a function in C# that opens a CSV file and save them to an array:
FileStream fileStream = new FileStream(guid.ToString(), FileMode.Open);
for (int i = 1; i > 200; i++) // it checks the first 200 lines
{
int j = 0;
string[] str = new string[j];
do
{
// saving each character to the variable until comma is found
} while(str == '\n'); // read each character in a for loop until new line character found
}
Can you please help me out?
Something like this:
using (StreamReader r = new StreamReader(guid.ToString()))
{
string line;
int linesCount;
ArrayList result = new ArrayList();
while ((line = r.ReadLine()) != null && linesCount++ <= 200)
{
result.AddRange(line.Split(','));
}
}
Parsing CSV by hand is actually pretty tricky. You might be better off reusing the TextFieldParser (add a reference to the Microsoft.VisualBasic assembly).
using Microsoft.VisualBasic.FileIO;
....
string[,] parsedCsv;
List<string[]> csvLines = new List<string[]>();
TextFieldParser parser = new TextFieldParser(new FileStream(guid.ToString(), FileMode.Open));
parser.Delimiters = new string[] { "," };
parser.TextFieldType = FieldType.Delimited;
int maxLines = 200, lineCount = 0;
try
{
while (!parser.EndOfData && lineCount++ < maxLines)
{
csvLines.Add(parser.ReadFields());
}
}
catch (MalformedLineException)
{
Console.WriteLine("Line Number: {0} Value: {1}", parser.ErrorLineNumber, parser.ErrorLine);
return;
}
parsedCsv = new string[csvLines.Count, csvLines[0].Length];
for (int i = 0; i < csvLines.Count; i++)
{
for (int j = 0; j < csvLines[i].Length; j++)
{
parsedCsv[i, j] = csvLines[i][j];
}
}
I have assumed here that the output is going to be a 2-D array of strings - you may need to adjust this code depending on what you are after, especially if you have to cope with the situation where each line does not have the same number of fields (perhaps unlikely, but still).
The really useful thing about TextFieldParser is that it will cope with different kinds of delimeters. By setting parser.Delimiters = new string[] { "\t" };, for example, this same code could parse tab-delimited text.
What about:
string[] lines = File.ReadAllLines(path);
if(lines.Length >= 200){
for(int i = 0; i < 200; i++){
string[] str = lines[i].Split(',');
//do something here
}
}
You can just use the string.Split(',') extension method.
using (StreamReader streamReader = new StreamReader(File.OpenRead(guid.ToString())))
{
for (int i = 0; i <= 200; ++i)
{
string[] str = streamReader.ReadLine().Split(',');
}
}
The Split extension method will return a string array of the individual values separated by a comma.
I have a file that is a SQL Server result set saved as a text file.
Here is a sample of what the file looks like:
RWS_DMP_ID RV1_DMP_NUM CUS_NAME
3192 3957 THE ACME COMPANY
3192 3957 THE ACME COMPANY
3192 3957 THE ACME COMPANY
I want to create a C# program that reads this file and creates the following table of data:
Field MaxSize
----- -------
RWS_DMP_ID 17
RV1_DMP_NUM 17
CUS_NAME 42
This is a list of the field names and their max length. The max length is the beginning of the field to the space right before the beginning of the next field.
By the way I don't care about code performance. This is seldom used file processing utility.
I solved this with the following code:
objFile = new StreamReader(strPath + strFileName);
strLine = objFile.ReadLine();
intLineCnt = 0;
while (strLine != null)
{
intLineCnt++;
if (intLineCnt <= 3)
{
if (intLineCnt == 1)
{
strWords = SplitWords(strLine);
intNumberOfFields = strWords.Length;
foreach (char c in strLine)
{
if (bolNewField == true)
{
bolFieldEnd = false;
bolNewField = false;
}
if (bolFieldEnd == false)
{
if (c == ' ')
{
bolFieldEnd = true;
}
}
else
{
if (c != ' ')
{
if (intFieldCnt < strWords.Length)
{
strProcessedData[intFieldCnt, 0] = strWords[intFieldCnt];
strProcessedData[intFieldCnt, 1] = (intCharCnt - 1).ToString();
}
intFieldCnt++;
intCharCnt = 1;
bolNewField = true;
}
}
if (bolNewField == false)
{
intCharCnt++;
}
}
strProcessedData[intFieldCnt, 0] = strWords[intFieldCnt];
strProcessedData[intFieldCnt, 1] = intCharCnt.ToString();
}
else if (intLineCnt == 3)
{
intLine2Cnt= 0;
intTotalLength = 0;
while(intLine2Cnt < intNumberOfFields)
{
intSize = Convert.ToInt32(strProcessedData[intLine2Cnt, 1]);
if (intSize + intTotalLength > strLine.Length)
{
intSize = strLine.Length - intTotalLength;
}
strField = strLine.Substring(intTotalLength, intSize);
strField = strField.Trim();
strProcessedData[intLine2Cnt, intLineCnt - 1] = strField;
intTotalLength = intTotalLength + intSize + 1;
intLine2Cnt++;
}
}
}
strLine = objFile.ReadLine();
}`enter code here`
I'm aware that this code is a complete hack job. I'm looking for a better way to solve this problem.
Is there a better way to solve this problem?
THanks
I'm not sure how memory efficient this is, but I think it's a bit cleaner (assuming your fields are tab-delimited):
var COL_DELIMITER = new[] { '\t' };
string[] lines = File.ReadAllLines(strPath + strFileName);
// read the field names from the first line
var fields = lines[0].Split(COL_DELIMITER, StringSplitOptions.RemoveEmptyEntries).ToList();
// get a 2-D array of the columns (excluding the header row)
string[][] columnsArray = lines.Skip(1).Select(l => l.Split(COL_DELIMITER)).ToArray();
// dictionary of columns with max length
var max = new Dictionary<string, int>();
// for each field, select all columns, and take the max string length
foreach (var field in fields)
{
max.Add(field, columnsArray.Select(row => row[fields.IndexOf(field)]).Max(col => col.Trim().Length));
}
// output per requirment
Console.WriteLine(string.Join(Environment.NewLine,
max.Keys.Select(field => field + " " + max[field])
));
void MaximumWidth(StreamReader reader)
{
string[] columns = null;
int[] maxWidth = null;
string line;
while ((line = reader.ReadLine()) != null)
{
string[] cols = line.Split('\t');
if (columns == null)
{
columns = cols;
maxWidth = new int[cols.Length];
}
else
{
for (int i = 0; i < columns.Length; i++)
{
int width = cols[i].Length;
if (maxWidth[i] < width)
{
maxWidth[i] = width;
}
}
}
}
// ...
}
Here is what I came up with. The big takeaway is to use the IndexOf string function.
class Program
{
static void Main(string[] args)
{
String strFilePath;
String strLine;
Int32 intMaxLineSize;
strFilePath = [File path and name];
StreamReader objFile= null;
objFile = new StreamReader(strFilePath);
intMaxLineSize = File.ReadAllLines(strFilePath).Max(line => line.Length);
//Get the first line
strLine = objFile.ReadLine();
GetFieldNameAndFieldLengh(strLine, intMaxLineSize);
Console.WriteLine("Press <enter> to continue.");
Console.ReadLine();
}
public static void GetFieldNameAndFieldLengh(String strLine, Int32 intMaxSize)
{
Int32 x;
string[] fields = null;
string[,] strFieldSizes = null;
Int32 intFieldSize;
fields = SplitWords(strLine);
strFieldSizes = new String[fields.Length, 2];
x = 0;
foreach (string strField in fields)
{
if (x < fields.Length - 1)
{
intFieldSize = strLine.IndexOf(fields[x + 1]) - strLine.IndexOf(fields[x]);
}
else
{
intFieldSize = intMaxSize - strLine.IndexOf(fields[x]);
}
strFieldSizes[x, 0] = fields[x];
strFieldSizes[x, 1] = intFieldSize.ToString();
x++;
}
Console.ReadLine();
}
static string[] SplitWords(string s)
{
return Regex.Split(s, #"\W+");
}
}