How do I refactor a ReadLine loop to Linq - c#

I'd like to make below code cleaner (in the eye of the beholder).
var lines = new StringReader(lotsOfIncomingLinesWithNewLineCharacters);
var resultingLines = new List<string>();
string line;
while( (line = lines.ReadLine() ) != null )
{
if( line.Substring(0,5) == "value" )
{
resultingLines.Add(line);
}
}
to something like
var resultingLinesQuery =
lotsOfIncomingLinesWithNewLineCharacters
.Where(s=>s.Substring(0,5) == "value );
Hopefully I have illustrated that I'd prefer to not have the result as a list (to not fill up memory) and that StringReader is not mandatory.
There is the naïve solution to create an extension and move the ReadLine there but I have a feeling there might be a better way.

Basically you need a way of extracting lines from a TextReader. Here's a simple solution which will only iterate once:
public static IEnumerable<string> ReadLines(this TextReader reader)
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
You could use that with:
var resultingLinesQuery =
new StringReader(lotsOfIncomingLinesWithNewLineCharacters)
.ReadLines()
.Where(s => s.Substring(0,5) == "value");
But ideally, you should be able to iterate over an IEnumerable<T> more than once. If you only need this for strings, you could use:
public static IEnumerable<string> SplitIntoLines(this string text)
{
using (var reader = new StringReader(text))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
Then:
var resultingLinesQuery =
lotsOfIncomingLinesWithNewLineCharacters
.SplitIntoLines()
.Where(s => s.Substring(0,5) == "value");

Related

How to read the null string with Streamreader c#

I am reading the file which has data as below
123456788|TUUKKA|RASK|01/01/85|HOCKEY|123
123456786|TOM|BRADY|01/01/75|FOOTBALL|123
123456787|RAJON|RONDO|01/01/80|BASKETBALL|ABC
123456785|DUSTIN|PEDROIA|01/01/83|BASEBALL|
123456789|DAVID|ORTIZ|01/01/77|BASEBALL|123
and splitting it with the delimiter '|', but I am the stream reader is not reading the line 4 which contains a null at the end.How do I handle this?
This is my code for reading and splitting the text file line
string s = string.Empty;
using (System.IO.StreamReader File = new System.IO.StreamReader(Path))
{
File.ReadLine();//Removing the first line
while ((s = File.ReadLine()) != null)
{
string[] str = s.Split('|');
UpdateRecords.Athelete(str);
}
}
this is my UpdateRecords.Athelete(str) code:
public static void Athelete(string[] Records) {
tblAthlete athlete = new tblAthlete();
using (SportEntities sportEntities = new SportEntities()) {
var temp = Convert.ToInt32(Records[0]);
if (sportEntities.tblAthletes.FirstOrDefault(x => x.SSN == temp) == null) {
athlete.SSN = Convert.ToInt32(Records[0]);
athlete.First_Name = Records[1];
athlete.Last_Name=Records[2];
athlete.DOB = Convert.ToDateTime(Records[3]);
athlete.SportsCode = Records[4];
athlete.Agency_Code = Records[5];
sportEntities.tblAthletes.Add(athlete);
sportEntities.SaveChanges();
}
}
}
If we put:
athlete.Agency_Code = Records[5];
together with (from comments):
The column is an FK referenced to another table PK and it can accept null values.
the problem becomes clear. An empty string ("") is not a null; it is an empty string! It sounds like you simply want something like:
var agencyCode = Records[5];
athlete.Agency_Code = string.IsNullOrEmpty(agencyCode) ? null : agencyCode;

Confusion while working with lists and the "new" parameter

Well, i am new to C# and it seems that i got a problem right over here. I know the problem already, but I don't know how to solve it. I am simply overwriting the object i want to add to my list. Can anyone help me?
List<string> dataSet = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
dataSet.Add(s);
if (s.Contains("GPGGA"))
{
myData.Add(new DataSet(dataSet));
dataSet.Clear();
Console.WriteLine();
}
}
}
}
If I'm understanding your problem correctly your adding a list of strings to a dataset. You are then clearing that list. It looks to me that your adding a reference to a list, then your calling clear on that list. That is why your loosing your values. You need to add a copy of that list to the other dataset. Try the code below
List<string> dataSet = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
dataSet.Add(s);
if (s.Contains("GPGGA"))
{
myData.Add(new DataSet(dataSet.ToList()));
dataSet.Clear();
Console.WriteLine();
}
}
}
}
dataSet.ToList() will return a copy of the List instead of adding a reference to it, that way dataSet.Clear() won't clear the list you added to myData
I think one of the problems you may be having is that you've confused yourself a little by naming your List<T> a keyword. A DataSet is its own thing and makes this code very hard to read. First, let's clean that up:
List<string> theData = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
theData.Add(s);
if (s.Contains("GPGGA"))
{
myData.Add(new DataSet(theData)); //you're passing your source by reference.
theData.Clear();
Console.WriteLine();
}
}
}
}
You need to pass the value of your datasource, but a List<T> is a reference type.
List<string> theData = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
theData.Add(s);
if (s.Contains("GPGGA"))
{
List<string> bindingdata = theData.ToList();
myData.Add(new DataSet(bindingData)); //you're passing
//your source by value, because it's using a new list.
theData.Clear(); //you can now safely do this.
Console.WriteLine();
}
}
}
}
All of that done, it still won't work, because DataSet doesn't have a constructor that accepts a List<string>. You need to change myData.Add(new DataSet(theData)) to the actual constructor. Interestingly, your DataSet may be the poor choice for what you're doing here. Unless you are using multiple tables, which you don't appear to be, you're better off using just one DataTable.
List<string> theData = new List<string>();
string s;
while ((s = sr.ReadLine()) != null)
{
if (!String.IsNullOrEmpty(s))
{
if (s[0] == '$')
{
theData.Add(s);
if (s.Contains("GPGGA"))
{ //we don't need this now.
//List<string> bindingdata = theData.ToList();
//make myData a DataSet so you can use datatables
// instead of attempting IEnumerable<DataSet>
DataTable foo = new DataTable("Foo");
foreach(string s in theData)
{
var y = foo.NewRow();
y[0] = s;
foo.Rows.Add(y);
}
myData.Add(foo);
theData.Clear(); //you can now safely do this.
Console.WriteLine();
}
}
}
}
Even with all of that, you'd be saving yourself a lot of trouble if you look into the DataTable and StringBuilder classes. There are better ways to do all of this than you're attempting.

Read text file from specific position and store in two arrays

I have text file which contains line like this:
#relation SMILEfeatures
#attribute pcm_LOGenergy_sma_range numeric
#attribute pcm_LOGenergy_sma_maxPos numeric
#attribute pcm_LOGenergy_sma_minPos numeric...
Where are about 6000 lines of these attributes, after attributes where are lines like this:
#data
1.283827e+01,3.800000e+01,2.000000e+00,5.331364e+00
1.850000e+02,4.054457e+01,4.500000e+01,3.200000e+01...
I need to seperate these strings in two different arrays. So far I only managed to store everything in one array.
Here is my code for storing in array:
using (var stream = new FileStream(filePath, FileMode.OpenOrCreate))
{
using (var sr = new StreamReader(stream))
{
String line;
while ((line = sr.ReadLine()) != null)
{
sb.AppendLine(line);
}
}
string allines = sb.ToString();
Console.WriteLine(sb);
}
All strings after #relation SMILEfeatures and contains #attribute are stored in first array. All the strings after #data should are stored in the second array. Hope this is what you wanted.
var relationLineNumbers = new List<int>();
var dataLineNumbers = new List<int>();
var relation = new StringBuilder();
var data = new List<string>();
using (var stream = new FileStream(filepath, FileMode.OpenOrCreate))
{
using (var sr = new StreamReader(stream))
{
string line;
bool isRelation = false;
bool isData = false;
int lineNumber = 0;
while ((line = sr.ReadLine()) != null)
{
lineNumber++;
if (line.StartsWith("#relation SMILEfeatures"))
{
isRelation = true;
isData = false;
continue;
}
if (line.StartsWith("#data"))
{
isData = true;
isRelation = false;
continue;
}
if (isRelation)
{
if (line.StartsWith("#attribute"))
{
relation.Append(line);
relationLineNumbers.Add(lineNumber);
}
}
if (isData)
{
data.AddRange(line.Split(','));
dataLineNumbers.Add(lineNumber);
}
}
}
Console.WriteLine("Relation");
Console.WriteLine(relation.ToString());
Console.WriteLine("Data");
data.ForEach(Console.WriteLine);
All strings which starts with #relation SMILEfeatures and contains #attribute should be stored in first array. Numbers which starts with #data should be stored in second array.
Use string.Contains() and string.StatsWith() for checking.
Read every line and decide in wich array / list you want to put this line
void ReadAndSortInArrays(string fileLocation)
{
List<string> noData = new List<string>();
List<string> Data = new List<string>();
using(StreamReader sr = new StreamReader(fileLocation))
{
string line;
while(!sr.EndOfStream)
{
line = sr.ReadLine();
if(line.StartsWith("#relation") && line.Contains("#attribute"))
{
noData.Add(line);
}
else if(line.StartsWith("#data")
{
Data.Add(line);
}
else
{
// This is stange
}
}
}
var noDataArray = noData.ToArray();
var DataArray = Data.ToArray();
}
But i think that not every line is beginning with "#data"
So you may want to Read all lines and do somethink like this:
string allLines;
using(StreamReader sr = new StreamReader(yourfile))
{
allLines = = sr.ReadToEnd();
}
var arrays = allLines.Split("#data");
// arrays[0] is the part before #data
// arrays[1] is the part after #data (the numbers)
// But array[1] does not contain #data
The question is not really very clear. But my take is, collect all lines that start with #relation or #attribute in one bucket, then collect all number lines in another bucket. I have chosen to ignore the #data lines, as they do not seem to contain any extra information.
Error checking may be performed by making sure that the data lines (i.e. number lines) contain comma separated lists of parsable numerical values.
var dataLines = new List<string>();
var relAttLines = new List<string>();
foreach (var line in File.ReadAllLines())
{
if (line.StartsWith("#relation") || line.StartsWith("#attribute"))
relAttLines.Add(line);
else if (line.StartsWith("#data"))
//ignore these
continue;
else
dataLines.Add(line);
}

How to combine two function's for file deletion

I have two different function to handle two different types of my input text file. One text file with double quotes and one without double quotes.
I wanted to know how can i combine these two functions to a common single function where i can handle in a more efficient way
Code:
//this the function to handle text file without double quotes
public void stack1()
{
string old;
string iniPath = Application.StartupPath + "\\list.ini";
bool isDeleteSectionFound = false;
List<string> deleteCodeList = new List<string>();
using (StreamReader sr = File.OpenText(iniPath))
{
while ((old = sr.ReadLine()) != null)
{
if (old.Trim().Equals("[DELETE]"))
{
isDeleteSectionFound = true;
}
if (isDeleteSectionFound && !old.Trim().Equals("[DELETE]"))
{
deleteCodeList.Add(old.Trim());
}
}
}
StringBuilder sb = new StringBuilder();
using (StreamReader reader = File.OpenText(textBox1.Text))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var value = line.Split('\t');
bool deleteLine = value.Any(v => deleteCodeList.Any(w => v.Equals(w)));
if (!deleteLine)
{
sb.Append(line + Environment.NewLine);
}
}
}
File.WriteAllText(textBox1.Text, sb.ToString());
//return;
}
//this the function to handle text file with double quotes
public void stack()
{
string old;
string iniPath = Application.StartupPath + "\\list.ini";
bool isDeleteSectionFound = false;
List<string> deleteCodeList = new List<string>();
using (StreamReader sr = File.OpenText(iniPath))
{
while ((old = sr.ReadLine()) != null)
{
if (old.Trim().Equals("[DELETE]"))
{
isDeleteSectionFound = true;
}
if (isDeleteSectionFound && !old.Trim().Equals("[DELETE]"))
{
deleteCodeList.Add(old.Trim());
}
}
}
StringBuilder sb = new StringBuilder();
using (StreamReader reader = File.OpenText(textBox1.Text))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split('\t').Select(v => v.Trim(' ', '"'));
bool deleteLines = values.Any(v => deleteCodeList.Any(w => v.Equals(w)));
if (!deleteLines)
{
sb.Append(line + Environment.NewLine);
}
}
}
File.WriteAllText(textBox1.Text, sb.ToString());
MessageBox.Show("finish");
}
The only difference between these two functions is this line:
// stack1 function
var value = line.Split('\t');
// stack2 function
var values = line.Split('\t').Select(v => v.Trim(' ', '"'));
The simplest way would probably be to add a parameter to your method, and then add the check after the split:
public void Split(bool shouldTrimQuotes)
{
...
IEnumerable<string> value = line.Split('\t');
if (shouldTrimQuotes)
{
value = value.Select(v => v.Trim(' ', '"'));
}
...
}
In one case, you would pass true as the parameter (which will cause quotes to be trimmed), while in the second one you would pass false to indicate you don't want to trim them:
// split, but don't trim quotes before comparison
Split(shouldTrimQuotes: false);
// split, trim quotes before comparison
Split(shouldTrimQuotes: true);
You might also play a bit and try to refactor the whole thing, trying to extract smaller general pieces of code into separate methods which might make it clearer what they are doing. This is one approach, for example:
// rewrites the specified file, removing all lines matched by the predicate
public static void RemoveLinesFromFile(string filename, Func<string, bool> match)
{
var linesToKeep = File.ReadAllLines(filename)
.Where(line => match(line))
.ToList();
File.WriteAllLines(filename, linesToKeep);
}
// gets the list of "delete codes" from the specified ini file
public IList<string> GetDeleteCodeList(string iniPath)
{
return File.ReadLines(iniPath)
.SkipWhile(l => l.Trim() != "[DELETE]")
.Skip(1).ToList();
}
// removes lines from a tab-delimited file, where the specified listOfCodes contains
// at least one of the tokens inside that line
public static void RemoveLinesUsingCodeList(
string filename,
IList<string> listOfCodes,
bool shouldTrimQuotes)
{
RemoveLinesFromFile(filename, line =>
{
IEnumerable<string> tokens = line.Split('\t');
if (shouldTrimQuotes)
{
tokens = tokens.Select(v => v.Trim(' ', '"'));
}
return (tokens.Any(t => listOfCodes.Any(t.Equals)));
});
}

Dictionaries in C#

This program is meant to read in a csv file and create a dictionary from it, which is then used to translate a word typed into a textbox (txtINPUT) and output the result to another textbox (txtOutput).
The program doesnt translate anything and always outputs "No translation found."
I've never used the dictionary class before so I dont know where the problem is coming from.
Thanks for any help you can give me.
Dictionary<string, string> dictionary;
private void CreateDictionary()
{
//Load file
List<string> list = new List<string>();
using (StreamReader reader = new StreamReader("dictionarylist.csv"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
//Add to dictionary
dictionary = new Dictionary<string, string>();
string[] split = line.Split(',');
dictionary.Add(split[0], split[1]);
}
}
}
private void btnTranslate_Click(object sender, EventArgs e)
{
CreateDictionary();
string outputString = null;
if (dictionary.TryGetValue(txtInput.Text, out outputString))
{
txtOutput.Text = outputString;
}
else
{
txtOutput.Text = ("No translation found");
}
}
You are creating a new instance of a Dictionary each loop cycle, basically overwriting it each time you read a line. Move this line out of the loop:
// Instantiate a dictionary
var map = new Dictionary<string, string>();
Also why not load dictionary one time, you are loading it each button click, this is not efficient.
(>=.NET 3) The same using LINQ ToDictionary():
usign System.Linq;
var map = File.ReadAllLines()
.Select(l =>
{
var pair = l.Split(',');
return new { First = pair[0], Second = pair[1] }
})
.ToDictionary(k => k.First, v => v.Second);
In your while loop, you create a new dictionary every single pass!
You want to create one dictionary, and add all the entries to that:
while ((line = reader.ReadLine()) != null)
{
//Add to dictionary
dictionary = new Dictionary<string, string>(); /* DON'T CREATE NEW DICTIONARIES */
string[] split = line.Split(',');
dictionary.Add(split[0], split[1]);
}
You should do it more like this:
List<string> list = new List<string>();
dictionary = new Dictionary<string, string>(); /* CREATE ONE DICTIONARY */
using (StreamReader reader = new StreamReader("dictionarylist.csv"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
string[] split = line.Split(',');
dictionary.Add(split[0], split[1]);
}
}

Categories