delay output of each line to a file - c#

i am bailing a windows 8 phone app.
i have a method that searches for a specific text from a text box and outputs it to a list box if found. I am trying to delay the output of each line written to a list box so i ideally it would be first line,wait 5 seconds, second line, wait 5 seconds,etc. currently it only delays at first then outputs all the lines it finds at once.
suspect.text is what i am searching for
results is the listbox it is sent to.
public void ReadFile()
{
var str = Application.GetResourceStream(new Uri("MACAddresses.txt", UriKind.Relative));
List<string> lines = new List<string>();
using (StreamReader r = new StreamReader(str.Stream))
{
string line;
while ((line = r.ReadLine()) != null)
{
if(line.Contains(Suspect.Text))
{
lines.add(line);
}
}
{
foreach (string output in lines)
{
Thread.sleep(1000);
results.items.add(output);
{
}

That's because you're blocking the UI thread, so it cannot update the ListBox. Solving this issue is good job for async programming:
public async Task ReadFile()
{
var str = Application.GetResourceStream(new Uri("MACAddresses.txt", UriKind.Relative));
List<string> lines = new List<string>();
using (StreamReader r = new StreamReader(str.Stream))
{
string line;
while ((line = r.ReadLine()) != null)
{
if(line.Contains(Suspect.Text))
{
lines.add(line);
}
}
}
foreach (string output in lines)
{
await Task.Delay(1000);
results.items.add(output);
}
}
Please note the async keyword that has been added in front of the method signature, and the return type that has been changed to Task. You'll have to use the await keyboard to properly call the ReadFile method:
await ReadFile(); // Don't forget to use the await keyword!
The method calling ReadFile will have to be marked as async as well, or the code won't compile.

Related

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

StreamReader read last line that contains

I am trying to read from a text file that has multiple outputs from when writing to it but when I want to read from the textfile that I already outputted stuff to, I want to choose the last entry(bear in mind each entry when writing has 5 lines and I just want the line containing "Ciphered text:")
But with this it is reading the lines containing that but I cannot work how to make it show only the last entry containing the string I specified.
using System;
using System.IO;
namespace ReadLastContain
{
class StreamRead
{
static void Main(string[] args)
{
string TempFile = #"C:\Users\Josh\Desktop\text2.txt";
using (var source = new StreamReader(TempFile))
{
string line;
while ((line = source.ReadLine()) != null)
{
if (line.Contains("Ciphered Text:"))
{
Console.WriteLine(line);
}
}
}
}
}
}
I would suggest to use LINQ for better readability:
string lastCipheredText = File.ReadLines(TempFile)
.LastOrDefault(l => l.Contains("Ciphered Text:"));
it is null if there was no such line. If you can't use LINQ:
string lastCipheredText = null;
while ((line = source.ReadLine()) != null)
{
if (line.Contains("Ciphered Text:"))
{
lastCipheredText = line;
}
}
It will be overwritten always, so you automatically get the last line that contained it.
You can use Linq:
var text = File
.ReadLines(#"C:\Users\Josh\Desktop\text2.txt")
.LastOrDefault(line => line.Contains("Ciphered Text:"));
if (null != text) // if thereĀ“s a text to print out
Console.WriteLine(text);

How to avoid c# File.ReadLines First() locking file

I do not want to read the whole file at any point, I know there are answers on that question, I want t
o read the First or Last line.
I know that my code locks the file that it's reading for two reasons 1) The application that writes to the file crashes intermittently when I run my little app with this code but it never crashes when I am not running this code! 2) There are a few articles that will tell you that File.ReadLines locks the file.
There are some similar questions but that answer seems to involve reading the whole file which is slow for large files and therefore not what I want to do. My requirement to only read the last line most of the time is also unique from what I have read about.
I nead to know how to read the first line (Header row) and the last line (latest row). I do not want to read all lines at any point in my code because this file can become huge and reading the entire file will become slow.
I know that
line = File.ReadLines(fullFilename).First().Replace("\"", "");
... is the same as ...
FileStream fs = new FileStream(#fullFilename, FileMode.Open, FileAccess.Read, FileShare.Read);
My question is, how can I repeatedly read the first and last lines of a file which may be being written to by another application without locking it in any way. I have no control over the application that is writting to the file. It is a data log which can be appended to at any time. The reason I am listening in this way is that this log can be appended to for days on end. I want to see the latest data in this log in my own c# programme without waiting for the log to finish being written to.
My code to call the reading / listening function ...
//Start Listening to the "data log"
private void btnDeconstructCSVFile_Click(object sender, EventArgs e)
{
MySandbox.CopyCSVDataFromLogFile copyCSVDataFromLogFile = new MySandbox.CopyCSVDataFromLogFile();
copyCSVDataFromLogFile.checkForLogData();
}
My class which does the listening. For now it simply adds the data to 2 generics lists ...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MySandbox.Classes;
using System.IO;
namespace MySandbox
{
public class CopyCSVDataFromLogFile
{
static private List<LogRowData> listMSDataRows = new List<LogRowData>();
static String fullFilename = string.Empty;
static LogRowData previousLineLogRowList = new LogRowData();
static LogRowData logRowList = new LogRowData();
static LogRowData logHeaderRowList = new LogRowData();
static Boolean checking = false;
public void checkForLogData()
{
//Initialise
string[] logHeaderArray = new string[] { };
string[] badDataRowsArray = new string[] { };
//Get the latest full filename (file with new data)
//Assumption: only 1 file is written to at a time in this directory.
String directory = "C:\\TestDir\\";
string pattern = "*.csv";
var dirInfo = new DirectoryInfo(directory);
var file = (from f in dirInfo.GetFiles(pattern) orderby f.LastWriteTime descending select f).First();
fullFilename = directory + file.ToString(); //This is the full filepath and name of the latest file in the directory!
if (logHeaderArray.Length == 0)
{
//Populate the Header Row
logHeaderRowList = getRow(fullFilename, true);
}
LogRowData tempLogRowList = new LogRowData();
if (!checking)
{
//Read the latest data in an asynchronous loop
callDataProcess();
}
}
private async void callDataProcess()
{
checking = true; //Begin checking
await checkForNewDataAndSaveIfFound();
}
private static Task checkForNewDataAndSaveIfFound()
{
return Task.Run(() => //Call the async "Task"
{
while (checking) //Loop (asynchronously)
{
LogRowData tempLogRowList = new LogRowData();
if (logHeaderRowList.ValueList.Count == 0)
{
//Populate the Header row
logHeaderRowList = getRow(fullFilename, true);
}
else
{
//Populate Data row
tempLogRowList = getRow(fullFilename, false);
if ((!Enumerable.SequenceEqual(tempLogRowList.ValueList, previousLineLogRowList.ValueList)) &&
(!Enumerable.SequenceEqual(tempLogRowList.ValueList, logHeaderRowList.ValueList)))
{
logRowList = getRow(fullFilename, false);
listMSDataRows.Add(logRowList);
previousLineLogRowList = logRowList;
}
}
//System.Threading.Thread.Sleep(10); //Wait for next row.
}
});
}
private static LogRowData getRow(string fullFilename, bool isHeader)
{
string line;
string[] logDataArray = new string[] { };
LogRowData logRowListResult = new LogRowData();
try
{
if (isHeader)
{
//Asign first (header) row data.
//Works but seems to block writting to the file!!!!!!!!!!!!!!!!!!!!!!!!!!!
line = File.ReadLines(fullFilename).First().Replace("\"", "");
}
else
{
//Assign data as last row (default behaviour).
line = File.ReadLines(fullFilename).Last().Replace("\"", "");
}
logDataArray = line.Split(',');
//Copy Array to Generics List and remove last value if it's empty.
for (int i = 0; i < logDataArray.Length; i++)
{
if (i < logDataArray.Length)
{
if (i < logDataArray.Length - 1)
{
//Value is not at the end, from observation, these always have a value (even if it's zero) and so we'll store the value.
logRowListResult.ValueList.Add(logDataArray[i]);
}
else
{
//This is the last value
if (logDataArray[i].Replace("\"", "").Trim().Length > 0)
{
//In this case, the last value is not empty, store it as normal.
logRowListResult.ValueList.Add(logDataArray[i]);
}
else { /*The last value is empty, e.g. "123,456,"; the final comma denotes another field but this field is empty so we will ignore it now. */ }
}
}
}
}
catch (Exception ex)
{
if (ex.Message == "Sequence contains no elements")
{ /*Empty file, no problem. The code will safely loop and then will pick up the header when it appears.*/ }
else
{
//TODO: catch this error properly
Int32 problemID = 10; //Unknown ERROR.
}
}
return logRowListResult;
}
}
}
I found the answer in a combination of other questions. One answer explaining how to read from the end of a file, which I adapted so that it would read only 1 line from the end of the file. And another explaining how to read the entire file without locking it (I did not want to read the entire file but the not locking part was useful). So now you can read the last line of the file (if it contains end of line characters) without locking it. For other end of line delimeters, just replace my 10 and 13 with your end of line character bytes...
Add the method below to public class CopyCSVDataFromLogFile
private static string Reverse(string str)
{
char[] arr = new char[str.Length];
for (int i = 0; i < str.Length; i++)
arr[i] = str[str.Length - 1 - i];
return new string(arr);
}
and replace this line ...
line = File.ReadLines(fullFilename).Last().Replace("\"", "");
with this code block ...
Int32 endOfLineCharacterCount = 0;
Int32 previousCharByte = 0;
Int32 currentCharByte = 0;
//Read the file, from the end, for 1 line, allowing other programmes to access it for read and write!
using (FileStream reader = new FileStream(fullFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 0x1000, FileOptions.SequentialScan))
{
int i = 0;
StringBuilder lineBuffer = new StringBuilder();
int byteRead;
while ((-i < reader.Length) /*Belt and braces: if there were no end of line characters, reading beyond the file would give a catastrophic error here (to be avoided thus).*/
&& (endOfLineCharacterCount < 2)/*Exit Condition*/)
{
reader.Seek(--i, SeekOrigin.End);
byteRead = reader.ReadByte();
currentCharByte = byteRead;
//Exit condition: the first 2 characters we read (reading backwards remember) were end of line ().
//So when we read the second end of line, we have read 1 whole line (the last line in the file)
//and we must exit now.
if (currentCharByte == 13 && previousCharByte == 10)
{
endOfLineCharacterCount++;
}
if (byteRead == 10 && lineBuffer.Length > 0)
{
line += Reverse(lineBuffer.ToString());
lineBuffer.Remove(0, lineBuffer.Length);
}
lineBuffer.Append((char)byteRead);
previousCharByte = byteRead;
}
reader.Close();
}

How can I read a CSV from Webserver using c# with LINQ? (ClickOnce deployment)

Hello I'm very new to coding but I developed a tool which works for local use.
My Boss wants me to put it on our website for customers.
I figured out ClickOnce is a way to do this.
The .exe is reading a .csv file to find data for use:
public string FindSalesmenPhone(string userName)
{
List<string> resLines = new List<string>();
var lines = File.ReadLines(#"S:\data.csv");
foreach (var line in lines)
{
var res = line.Split(new char[] { ',' });
//id to search
if (res[7] == userName)
{
resLines.Add(res[10]);
}
}
//to get the output
foreach (var line in resLines)
{
return line;
}
MessageBox.Show("no phone found!");
return null;
}
My question is: How can I change this path and will the .csv-file still be accessible after I deployed the tool with ClickOnce.
List<string> resLines = new List<string>();
var lines = File.ReadLines(#"S:\Manuel\data.csv");
Can I simply change it to something like:
var lines = File.ReadLines(#"http://mywebsite.com/data.csv");
Sorry might be easy-pie for you guys but I really appreciate your help!
It would not be File.ReadLines as that is using File.IO and you're asking to get the text from a webpage. The best way to go about this would be to create a WebRequest to the URL you have specified and then read the csv from there using a StreamReader. See this thread which is similar.
EDIT:
Glad this helped you. You may have some bad performance simply because you called the StreamReader without a using statement. Try the code below:
public string TestCSV(string id)
{
List<string> splitted = new List<string>();
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://mywebsite.com/data.csv");
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
{
string currentline = sr.ReadLine();
if (currentline != string.IsNullOrWhiteSpace)
{
var res = currentline.Split(new char[] { ',' });
if (res[0] == id)
{
splitted.Add(res[1]);
}
foreach (var line in splitted)
{
return line;
}
}
}
return null;
}
Thanks Ryan, this was very helpful.
I modified it for my use, but now the performance is very bad. Guess the Tool reads the file over and over again...(A few points call this method during runtime)
Is there a way I can save the .csv in a 2D Array and then save this for the whole runtime of the program? The .csv contains about 900 entries..
public string TestCSV(string id)
{
List<string> splitted = new List<string>();
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("https://mywebsite.com/data.csv");
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
StreamReader sr = new StreamReader(resp.GetResponseStream());
string currentline;
while ((currentline = sr.ReadLine()) != null)
{
var res = currentline.Split(new char[] { ',' });
if (res[0] == id)
{
splitted.Add(res[1]);
}
foreach (var line in splitted)
{
return line;
}
}
return null;
}

How to read a txt file and load the contents into an arraylist?

I am new to C# and to programming in general. I am trying to read the contents of a txt file and load them to an arraylist. I can't figure out what condition to use in my while loop.
void LoadArrayList()
{
TextReader tr;
tr = File.OpenText("C:\\Users\\Maattt\\Documents\\Visual Studio 2010\\Projects\\actor\\actors.txt");
string Actor;
while (ActorArrayList != null)
{
Actor = tr.ReadLine();
if (Actor == null)
{
break;
}
ActorArrayList.Add(Actor);
}
}
void LoadArrayList()
{
TextReader tr;
tr = File.OpenText("C:\\Users\\Maattt\\Documents\\Visual Studio 2010\\Projects\\actor\\actors.txt");
string Actor;
Actor = tr.ReadLine();
while (Actor != null)
{
ActorArrayList.Add(Actor);
Actor = tr.ReadLine();
}
}
You can do it with just 2 lines of code
string[] Actor = File.ReadAllLines("C:\\Users\\Maattt\\Documents\\Visual Studio 2010\\Projects\\actor\\actors.txt");
ArrayList list = new ArrayList(Actor);
This is how it should be
void LoadArrayList()
{
string[] lines = System.IO.File.ReadAllLines(#"C:\Users\Maattt\Documents\Visual Studio 2010\Projects\actor\actors.txt");
// Display the file contents by using a foreach loop.
foreach (string Actor in lines)
{
ActorArrayList.Add(Actor);
}
}
Just rearrange it like this:
Actor = tr.ReadLine();
while (Actor != null)
{
ActorArrayList.Add(Actor);
Actor = tr.ReadLine();
}
If you look at the documentation for the TextReader.ReadLine method, you'll see that it returns either a string, or null if there are no more lines. So, what you can do is loop and check null against the results of the ReadLine method.
while(tr.ReadLine() != null)
{
// We know there are more items to read
}
With the above, though, you're not capturing the result of ReadLine. So you need to declare a string to capture the result and to use inside the while loop:
string line;
while((line = tr.ReadLine()) != null)
{
ActorArrayList.Add(line);
}
Also, I would suggest using a generic list, such as List<T> instead of the non-generic ArrayList. Using something like List<T> gives you more type safety and reduces the possibility of invalid assignments or casts.

Categories

Resources