Advance C# ReadLine() to next line in a function call - c#

In my C# app I'm trying to feed into ReadLine() a simple text document with 7 digit strings separated line by line. What I'm attempting to do is grab the next 7 digit string each time the function is called. Here's what I have so far:
string invoiceNumberFunc()
{
string path = #"C:\Users\sam\Documents\GCProg\testReadFile.txt";
try
{
using (StreamReader sr = new StreamReader(path))
{
invoiceNumber = sr.ReadLine();
}
}
catch (Exception exp)
{
Console.WriteLine("The process failed: {0}", exp.ToString());
}
return invoiceNumber;
}
How do I advance to the next line each time the invoiceNumberFunc() is called?
Thanks in advance.

You'd need to keep hold of the StreamReader between calls, either passing it into the method as a new parameter or making it a member variable of the class.
Personally I prefer the idea of it becoming a parameter, so that it never ends up as a member variable - that makes the life-time easier to manage:
void DoStuff()
{
string path = #"C:\Users\sam\Documents\GCProg\testReadFile.txt";
using (StreamReader sr = new StreamReader(path))
{
while (keepGoing) // Whatever logic you have
{
string invoice = InvoiceNumberFunc(sr);
// Use invoice
}
}
}
string InvoiceNumberFunc(TextReader reader)
{
string invoiceNumber;
try
{
invoiceNumber = reader.ReadLine();
}
catch (Exception exp)
{
Console.WriteLine("The process failed: {0}", exp.ToString());
}
return invoiceNumber;
}

You can't, since you create and dispose the stream reader in the function. Two ways come to mind:
You could store the stream reader in a member variable, or read all at once and store an array in a member variable.
Or you make it an iterator method by changing the return type to IEnumerable<string>, and changing the part in the using block to:
while ((invoiceNumber = sr.ReadLine()) != null) {
yield return invoiceNumber;
}
This way, you can call foreach on your invoiceNumberFunc.

You need to use the same StreamReader rather than creating a new one. Each time you create a new one and dispose of the old one you're starting right back at the start of the file.
Try passing the same StreamReader reference in or keeping a record of the position you are at in the stream and using Seek() on the base stream if necessary. I'd recommend the first of these personally.

You need to rework this, so that you're not creating the streamreader inside the method, but rather creating it at the class level, and just using it in the method, then disposing/closing the reader when you are done. Something like:
class MyClass
{
private StreamReader sr;
string invoiceNumberFunc()
{
if (sr == null)
sr = new StreamReader(path);
if (sr.EndOfStream) {
sr.Close();
sr = null;
return string.Empty;
}
try {
return sr.ReadLine();
}
catch(Exception exp) {
Console.WriteLine("Process failed {0}",exp.ToString());
return string.Empty;
}
}
}
In this case, it might also be a good idea to make your class IDisposable so you can verify that the StreamReader gets disposed, and also potentially make "initialize"/"close" routines, instead of doing the initialize and shutdown how I did here.

What you are looking for is the yield command:-
IEnumerable<string> GetInvoiceNumbers()
{
string path = #"C:\Users\sam\Documents\GCProg\testReadFile.txt";
using (StreamReader sr = new StreamReader(path))
{
while (!sr.EndOfStream)
{
yield return sr.ReadLine();
}
}
}
Now you can consume the return of this function with a simple for each:-
foreach(string invoiceNumber in GetInvoiceNumbers())
{
//Do Stuff with invoice number
}
Or get creative with LINQ.

An other way of doing this is to transform your function in an iterator block using the yield return statement
The only thing is to make sure you add a finaly clause to your try and remove the catch as the yield return cannot be used in a naked try / catch. So your code would become:
IEnumerable<String> invoiceNumberFunc()
{
string path = #"C:\Users\sam\Documents\GCProg\testReadFile.txt";
try
{
using ( System.IO.StreamReader sr = new System.IO.StreamReader( path ) )
{
String invoiceNumber;
while ( ( invoiceNumber = sr.ReadLine() ) != null )
{
yield return sr.ReadLine();
}
}
}
finally
{
}
}

Related

Assigning instance variables obtained through reflection in generic method

I have data in tab-separated values (TSV) text files that I want to read and (eventually) store in database tables. With the TSV files, each line contains one record, but in one file the record can have 2 fields, in another file 4 fields, etc. I wrote working code to handle the 2-field records, but I thought this might be a good case for a generic method (or two) rather than writing new methods for each kind of record. However, I have not been able to code this because of 2 problems: I can't create a new object for holding the record data, and I don't know how to use reflection to generically fill the instance variables of my objects.
I looked at several other similar posts, including Datatable to object by using reflection and linq
Below is the code that works (this is in Windows, if that matters) and also the code that doesn't work.
public class TSVFile
{
public class TSVRec
{
public string item1;
public string item2;
}
private string fileName = "";
public TSVFile(string _fileName)
{
fileName = _fileName;
}
public TSVRec GetTSVRec(string Line)
{
TSVRec rec = new TSVRec();
try
{
string[] fields = Line.Split(new char[1] { '\t' });
rec.item1 = fields[0];
rec.item2 = fields[1];
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Bad import data on line: " +
Line + "\n" + ex.Message, "Error",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Error);
}
return rec;
}
public List<TSVRec> ImportTSVRec()
{
List<TSVRec> loadedData = new List<TSVRec>();
using (StreamReader sr = File.OpenText(fileName))
{
string Line = null;
while ((Line = sr.ReadLine()) != null)
{
loadedData.Add(GetTSVRec(Line));
}
}
return loadedData;
}
// *** Attempted generic methods ***
public T GetRec<T>(string Line)
{
T rec = new T(); // compile error!
Type t = typeof(T);
FieldInfo[] instanceVars = t.GetFields();
string[] fields = Line.Split(new char[1] { '\t' });
for (int i = 0; i < instanceVars.Length - 1; i++)
{
rec. ??? = fields[i]; // how do I finish this line???
}
return rec;
}
public List<T> Import<T>(Type t)
{
List<T> loadedData = new List<T>();
using (StreamReader sr = File.OpenText(fileName))
{
string Line = null;
while ((Line = sr.ReadLine()) != null)
{
loadedData.Add(GetRec<T>(Line));
}
}
return loadedData;
}
}
I saw the line
T rec = new T();
in the above-mentioned post, but it doesn't work for me...
I would appreciate any suggestions for how to make this work, if possible. I want to learn more about using reflection with generics, so I don't only want to understand how, but also why.
I wish #EdPlunkett had posted his suggestion as an answer, rather than a comment, so I could mark it as the answer...
To summarize: to do what I want to do, there is no need for "Assigning instance variables obtained through reflection in generic method". In fact, I can have a generic solution without using a generic method:
public class GenRec
{
public List<string> items = new List<string>();
}
public GenRec GetRec(string Line)
{
GenRec rec = new GenRec();
try
{
string[] fields = Line.Split(new char[1] { '\t' });
for (int i = 0; i < fields.Length; i++)
rec.items.Add(fields[i]);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Bad import data on line: " + Line + "\n" + ex.Message, "Error",
System.Windows.Forms.MessageBoxButtons.OK,
System.Windows.Forms.MessageBoxIcon.Error);
}
return rec;
}
public List<GenRec> Import()
{
List<GenRec> loadedData = new List<GenRec>();
using (StreamReader sr = File.OpenText(fileName))
{
string Line = null;
while ((Line = sr.ReadLine()) != null)
loadedData.Add(GetRec(Line));
}
return loadedData;
}
I just tested this, and it works like a charm!
Of course, this isn't helping me to learn how to write generic methods or use reflection, but I'll take it...

How to count strings from text file in C#

in this button click event I am trying to count strings from text file that are the same as in textboxes, then display number of them in label. My problem is that I have no idea how to count them-I'm talking about code inside if-statement. I would really appreciate any help.
private void btnCalculate_Click(object sender, EventArgs e)
{
string openFileName;
using (OpenFileDialog ofd = new OpenFileDialog())
{
if (ofd.ShowDialog() != DialogResult.OK)
{
MessageBox.Show("You did not select OK");
return;
}
openFileName = ofd.FileName;
}
FileStream fs = null;
StreamReader sr = null;
try
{
fs = new FileStream("x", FileMode.Open, FileAccess.Read);
fs.Seek(0, SeekOrigin.Begin);
sr = new StreamReader(fs);
string s = sr.ReadLine();
while (s != null)
{
s = sr.ReadLine();
}
if(s.Contains(tbFirstClub.Text))
{
s.Count = lblResult1.Text; //problem is here
}
else if(s.Contains(tbSecondClub.Text))
{
s.Count = lblResult2.Text; //problem is here
}
}
catch (IOException)
{
MessageBox.Show("Error reading file");
}
catch (Exception)
{
MessageBox.Show("Something went wrong");
}
finally
{
if (sr != null)
{
sr.Close();
}
}
}
Thanks in advance.
s.Count = lblResult1.Text; //problem is here
wait...you are saying here..
you have a variable (s)
and you access its property (Count)
and then set it to the label text(lblResult1.Text)
is that what you're trying to do? because the reverse seems more likely
Using LINQ you can get the number of occurences, like below:
int numOfOcuurences= s.Count( s=> s == tbFirstClub.Text);
lblResult1.Text = numOfOcuurences.ToString();
welcome to Stack Overflow.
I want to point out something you said.
else if(s.Contains(tbSecondClub.Text))
{
s.Count = lblResult2.Text; //problem is here
}
S is our string that we just read from the file.
You're saying assoung S.Count (The length of the string) to text.
I don't think this is what you want. We want to return the number of times specified strings show up in a specified file
Let's refactor this, (And add some tricks along the way).
// Let's create a dictionary to store all of our desired texts, and the counts.
var textAndCounts = new Dictionary<string, int>();
textAndCounts.Add(tbFirstClub.Text, 0); // Assuming the type of Text is string, change acccorrdingly
textAndCounts.Add(tbSecondClub.Text, 0);
//We added both out texts fields to our dictionary with a value of 0
// Read all the lines from the file.
var allLines = File.ReadAllLines(openFileName); /* using System.IO */
foreach(var line in allLines)
{
if(line.Contains(tbFirstClub.Text))
{
textAndCounts[tbFirstClub.Text] += 1; // Go to where we stored our count for our text and increment
}
if(line.Contains(tbSecondClub.Text))
{
textandCounts[tbSecondClub.Text] += 1;
}
}
This should solve your problem, but it's still pretty brittle. Optimally, we want to design a system that works for any number of strings and counts them.
So how would I do it?
public Dictionary<string, int> GetCountsPerStringInFile(IEnumerable<string> textsToSearch, string filePath)
{
//Lets use Linq to create a dictionary, assuming all strings are unique.
//This means, create a dictionary in this list, where the key is the values in the list, and the value is 0 <Text, 0>
var textsAndCount = textsToSearch.ToDictionary(text => text, count => 0);
var allLines = File.ReadAllLines(openFileName);
foreach (var line in allLines)
{
// You didn't specify if a line could maintain multiple values, so let's handle that here.
var keysContained = textsAndCounts.Keys.Where(c => line.Contains(c)); // take all the keys where the line has that key.
foreach (var key in keysContained)
{
textsAndCounts[key] += 1; // increment the count associated with that string.
}
}
return textsAndCounts;
}
The above code allows us to return a data structure with any amount of strings with a count.
I think this is a good example for you to save you some headaches going forward, and it's probably a good first toe-dip into design patterns. I'd suggest looking up some material on Data structures and their use cases.

How to parse part of script code in c#?

I have a task to create an UI to run methods from scripts.
I made a method that shows the names of Methods from Script, and on the UI there is a checkbox next to them.
I have a problem that when I click on the checkbox, I do not know to invite only a pieces of code of this method.
private void GetAllDSCConfigurationName(string psFile, DSCTreeViewItemFolder item)
{
var file = new DSCTreeViewItemScipt() { Script = psFile };
item.AddChild(file);
try
{
using (StreamReader sr = new StreamReader(psFile))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (line.StartsWith("Configuration"))
{
file.AddChild(new DSCTreeViewItemConfiguration() { ConfigurationName = line });
}
}
}
}
catch (Exception e)
{
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
You can check in the same if statement:
if(line.StartsWith("Configuration") && line.Contains("LogOff"))
{
// do your stuff
break; // cause you don't need to look for other methods
}
If you have a name of your method from UI you can pass it to Contains method.
Also you can improve a bit your code:
string allLines = sr.ReadToEnd();
string configurations = allLines.Split("Configuration");
string methodToCall = configurations.FirstOrDefault(conf => conf.Contains("LogOff")); // or you can pass name of your method from parameter

Not all paths return a value

So i'm getting an error in my program with my Google Speech API. The error is "Not all paths return a value" as the title suggest.
Here is the code (2 peices)(Same error)
public static SpeechInputResult ProcessFlacFile(string FlacFileName, int BIT_RATE = DEFAULT_BIT_RATE, string language = DEFAULT_LANGUAGE, uint maxresults = 1)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://www.google.com/speech-api/v1/recognize?xjerr=1" + "&client=" + client + "&lang=" + language + "&maxresults=" + maxresults + "&pfilter=0");
FileStream fStream = new FileStream(FlacFileName, FileMode.Open, FileAccess.Read);
request.Proxy = null;
request.Timeout = 60000;
request.KeepAlive = true;
request.Method = "POST";
request.ContentType = "audio/x-flac; rate=8000";
//bitrate must = .flac file
request.UserAgent = client;
FileInfo fInfo = new FileInfo(FlacFileName);
long numbytes = fInfo.Length;
byte[] data = null;
using (FileStream fstream = new FileStream(FlacFileName, FileMode.Open, FileAccess.Read))
data = new byte[fstream.Length];
fStream.Read(data, 0, Convert.ToInt32(fStream.Length));
fStream.Close();
using (Stream wrStream = request.GetRequestStream())
{
wrStream.Write(data, 0, data.Length);
}
try
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
dynamic resp = response.GetResponseStream();
if (resp != null)
{
StreamReader sr = new StreamReader(resp);
MessageBox.Show(sr.ReadToEnd());
resp.Close();
resp.Dispose();
}
}
catch (System.Exception ee)
{
MessageBox.Show(ee.Message);
}
}
}
}
and 2nd piece here:
public class Hypothesis
{
public string utterance;
public double confidence = -1.0d;//-1 = No Value
public override string ToString()
{
return "'" +utterance + "'" + ((confidence == -1) ? "" : "#" + confidence);
}
public List<Hypothesis> hypotheses = new List<Hypothesis>();
public Hypothesis getBestHypothesis()
{
if (hypotheses.Count() <=0)
return null;
Hypothesis H = hypotheses[0];
foreach (Hypothesis h in hypotheses)
{
if (h.confidence>=H.confidence)
{
H = h;
}
return H;
}
}
Both code have the same error and it only seems to happen if I a certain variable name to be the same name as another variable (FlacFileName, to be exact). If you guys could tell me why this is happening that would be awesome thanks!
Your method signature
public static SpeechInputResult ProcessFlacFile(string FlacFileName, int BIT_RATE = DEFAULT_BIT_RATE, string language = DEFAULT_LANGUAGE, uint maxresults = 1)
states you will be returning a SpeechInputResult but you have no return statement.
You should either change the signature to void or actually return a value.
In the second instance, you need a return statement after the for loop.
Like everyone mentioned, add a return statement to the end of the first method. Just a return null; or whatever's appropriate in your situation. Actually, you have no other return statements in that method at all, so it's not a "not all paths return a value" situation like the second... you may just want to change the signature from SpeechInputResult to void.
In the second method, it seems like you have your bases covered, because:
You're returning null if hypotheses is empty and
You're returning the hypotheses with the largest "confidence" if it's not empty
But the compiler isn't smart enough to see that. Try moving return null; to the end. The only way you'll make it there is if there are no elements in the list and the foreach loop doesn't run.
public Hypothesis getBestHypothesis()
{
if (hypotheses.Any())
{
Hypothesis H = hypotheses[0];
foreach (Hypothesis h in hypotheses)
{
if (h.confidence >= H.confidence)
{
H = h;
}
return H;
}
}
return null;
}
Also, that whole second method could be reduced (with the help of LINQ):
return hypotheses.OrderByDescending(x => x.confidence).FirstOrDefault();
i find that in your code you have to return SpeechInputResult but there is no where you are using return statement.
It is preferred that you use return SpeechInputResult in your code
other wise put your function as below if your are not willing to return anything to it.
public static void ProcessFlacFile(string FlacFileName, int BIT_RATE = DEFAULT_BIT_RATE, string language = DEFAULT_LANGUAGE, uint maxresults = 1)
you can use void instead of the class SpeechInputResult as in the above code if you want to be it without the error in this case.
in the second piece of code you only instantiated the code. ie declared and directly used it
you must have something assigned to the object you created so that you can perform operation in the foreach.
in this case hypotheses doesnt have any thing inside it.
Also you have written return statement inside the foreach loop where as the focus will never go inside the foreach loop.
First you assign something to hypotheses so that it has value in it and then perform actions.
First block of code does not have any return statement. In the second code block , put a dummy return statement outside the for loop.
In your first method, you declared in signature that, it will be returning instance of typeSpeechInputResult, while you are not returning it from body.
And, in second method, you are returning from inside foreach loop, but what if your hypotheses list doesn't have any element? You should also be returning either a default value from outside the foreach loop or you should throw an exception if it isn't your expected behaviour.
ProcessFlacFile method in the above code should return a type SpeechInputResult object in all cases.
You can return the object after try and catch as
try
{
}
catch
{
}
return SpeechInputResultObj
or
return SpeechInputResult object in both try and catch statement
try
{
....
return SpeechInputResultObj;
}
catch
{
....
return SpeechInputResultObj;
}

Use yield in for loop

I have such function to read large txt files
private static IEnumerable<string> ReadLineFromFile(TextReader fileReader)
{
using (fileReader)
{
string currentLine;
while ((currentLine = fileReader.ReadLine()) != null)
{
yield return currentLine;
}
}
}
yield can be used only in foreach loop.
TextReader readFile = new StreamReader(file_path);
foreach (string url in ReadLineFromFile(readFile))
{
}
I need to re-write this function to use it with for loop. To state with Iterator the line to read.
I tried something but my attempts were unsuccessful
Any ideas?
The yield keyword can be used in any iterator block, not just foreach - your code should work.
The issue with your code is you are disposing of the TextReader after reading the first line.
Move the using statement outside the ReadLineFromFile method i.e.
using (var reader = new StreamReader(file_path))
{
foreach(string url in ReadLineFromFile(reader))
{
...
}
}
...
private static IEnumerable<string> ReadLineFromFile(TextReader fileReader)
{
while ((var currentLine = fileReader.ReadLine()) != null)
{
yield return currentLine;
}
}
I mistakenly assumed that using yield whilst having the enumerable code wrapped with a using would cause it to dispose early, however, I ran your code and it works fine!
Alternatively, if you fancy writing less code you could accomplish the same thing using File.ReadLines
foreach (var line in File.ReadLines(file_path))
{
...
}

Categories

Resources