C# search in text file - c#

Really new to C#. I need to search a text file for a keyword. If after the whole file is searched, the keyword is found pop a message box. If after the whole file is searched, the keyword is NOT found pop a message box.
So far I have this below. The problem is that it reads the file line by line. If in the first line the keyword is not found it shows the alert "Not found". Then goes to the next line and shows "Not found" again. And so on. I need the script to search the whole file, and only then show "Not found" only once. Thank you!
private void SearchButton_Click(object sender, EventArgs e)
{
System.IO.StreamReader file = new System.IO.StreamReader("c:\\test.txt");
String line;
String[] array;
while ((line = file.ReadLine()) != null)
{
if (line.Contains("keyword"))
{
MessageBox.Show("Keyword found!");
}
else
{
MessageBox.Show("Keyword not found!");
}
}
}

Try using File class instead of readers (which you have to Dispose in order to prevent resource leakage):
bool found = File
.ReadLines("c:\\test.txt") // Try avoid "All" when reading: ReadAllText, ReadAllLines
.Any(line => line.Contains("keyword"));
if (found)
MessageBox.Show("Keyword found!");
else
MessageBox.Show("Keyword not found!");
Your code amended (if you insist on StreamReader):
private void SearchButton_Click(object sender, EventArgs e) {
// Wra IDisposable (StreamReader) into using in order to prevent resource leakage
using (file = new StreamReader("c:\\test.txt")) {
string line;
while ((line = file.ReadLine()) != null)
if (line.Contains("keyword")) {
MessageBox.Show("Keyword found!");
return; // Keyword found, reported and so we have nothing to do
}
}
// File read with no positive result
MessageBox.Show("Keyword not found!");
}

File.ReadAllText is better suited for that, you can read in all the text at once in one string:
string file = File.ReadAllText("path");
if (file.Contains(keyword)) {
//..
}
else {
//..
}
or in one line:
if (File.ReadAllText("path").Contains("path")) {
}
else {
}
Like stated in the comments, you can run out of memory for very large files but for normal day to day use this wont happen.

Related

Having troubles trying to read and display a text file to the console in c#

I am trying to make a trivia game in c# using a console application. And I am having troubles getting the console to read the file. Currently what its doing is saying that the file could not be read and the the index was outside the bounds of the array. But then everything that is in the text file gets displayed. I am unsure on what I get the file could not be read but then the file gets displayed.
The file is a .txt file and here is what is looks like
What is another name for SuperMan?,the man of steel
What is Superman's only weakness?,kryptonite
What is the name of Batman's secret identity?,bruce wayne
Batman protects what city?,gotham city
How did Spiderman get his superpowers?,bitten by a radioactive sipder
This superheros tools include a bullet-proof braclets and a magic lasso.Who is she?,wonder woman
Which superhero has an indestructible sheild?,captain america
Which superhero cannot transformback into human form?,the thing
What villan got his distinctive appearance form toxic chemicals?,joker
What is the name of the archnemesis of the Fantastic Four?, dr doom
Here is the code that I have for reading and displaying the file.
static void Main(string[] args)
{
string filename = #"C:\Trivia\questions.txt";
List<string> questions = new List<string>();
List<string> answers = new List<string>();
LoadData(filename, questions, answers);
Console.WriteLine();
questions.ForEach(Console.WriteLine);
Console.WriteLine();
answers.ForEach(Console.WriteLine);
}
static void LoadData(string filename, List<string> questions, List<string> answers)
{
try
{
using(StreamReader reader = new StreamReader(filename))
{
string line;
while((line = reader.ReadLine()) != null)
{
string[] lineArray = line.Split(',');
string annswer = lineArray[1];
string question = lineArray[0];
questions.Add(question);
answers.Add(annswer);
}
}
}
catch(Exception e)
{
Console.WriteLine("File could not be read");
Console.WriteLine(e.Message);
}
}
Here is the output on the console.
File could not be read
Index was outside the bounds of the array.
What is another name for SuperMan?
What is Superman's only weakness?
What is the name of Batman's secret identity?
Batman protects what city?
How did Spiderman get his superpowers?
This superheros tools include a bullet-proof braclets and a magic lasso.Who is she?
Which superhero has an indestructible sheild?
Which superhero cannot transformback into human form?
What villan got his distinctive appearance form toxic chemicals?
What is the name of the archnemesis of the Fantastic Four?
the man of steel
kryptonite
bruce wayne
gotham city
bitten by a radioactive sipder
wonder woman
captain america
the thing
joker
dr doom
Thanks for the suggestions.
From playing around with your code, it looks like you might have some newlines at the end of your questions.txt file. Getting rid of those would fix your initial problem, but the real issue is that you're not checking each line to see if it contains a comma, nor are you discarding empty rows of data. Here's an approach that does both:
static void LoadData(string filename, List<string> questions, List<string> answers)
{
try
{
using (StreamReader reader = new StreamReader(filename))
{
string[] lines=
reader.ReadToEnd() //Read the whole file
.Trim() //Get rid of whitespace at the beginning and end of the file, no more random newlines at the end.
.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) //Separate each line AND remove any empty lines.
;
foreach (string _line in lines)
{
string line = _line.Trim();
if (!line.Contains(','))
{
Console.Error.WriteLine("!!! Line did not contain comma for separation");
Console.Error.WriteLine("!!!!!! " + line);
continue; //Just go on to the next line.
}
string[] lineArray = line.Split(',');
string answer = lineArray[1];
string question = lineArray[0];
questions.Add(question);
answers.Add(answer);
}
}
}
catch (Exception e)
{
Console.WriteLine("File could not be read");
Console.WriteLine(e.Message);
}
}
Of course if you want to read each line in separately, just check each line to make sure that it has any length after being trimmed (skip it if not) and that it contains a comma (log the error)
I suspect that there's a blank line (or at least a line without a comma) at the end of the file, and then this line: string annswer = lineArray[1]; is throwing an exception because you've hard-coded index 1 without first checking the size of the lineArray. Then the error is shown, but only after the questions and answers have been populated, so you also see those output to the console.
To avoid this, it's a general good practice is to ensure that an array index exists before checking it. Something like this might be helpful:
while ((line = reader.ReadLine()) != null)
{
string[] lineArray = line.Split(',');
// If this line doesn't contain a comma, then skip it
if (lineArray.Length < 2) continue;
string annswer = lineArray[1];
string question = lineArray[0];
questions.Add(question);
answers.Add(annswer);
}
Alternatively, you could throw an exception:
// If this line doesn't contain a comma, throw an exception
if (lineArray.Length < 2)
{
throw new FormatException($"This line does not contain a comma: {line}");
}
Additionally, you could simplify your code slightly by using the System.IO.File class to read the file:
static void LoadData(string filename, List<string> questions, List<string> answers)
{
try
{
foreach (var line in File.ReadLines(filename))
{
var lineArray = line.Split(',');
// If this line doesn't contain a comma, skip it
if (lineArray.Length < 2) continue;
questions.Add(lineArray[0]);
answers.Add(lineArray[1]);
}
}
catch (Exception e)
{
Console.WriteLine($"Error reading file: {e.Message}");
}
}
This is one of technique to handle exception.
I've tried your code but it works well.
Apply my code it and look my comment.
static void LoadData(string filename, List<string> questions, List<string> answers)
{
string readText = ""; //for writing error at catch clause.
try
{
using (StreamReader reader = new StreamReader(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
readText = line; //copy text line which is current.
string[] lineArray = line.Split(',');
string annswer = lineArray[1];
string question = lineArray[0];
questions.Add(question);
answers.Add(annswer);
}
}
}
catch (Exception e)
{
//print error with problem text.
Console.WriteLine("File could not be read from : {0}", readText);
Console.WriteLine(e.Message);
}
}
Also, this is another,
Move to : Menu -> DEBUG -> Windows -> Exception settings.
And then check "Common Language Runtime Exception".
This will allow stop where occur problem. It doesn't matter that is in try~catch clause or not.
This is one of best way to find problem in loop statement.

How can i get all files on disk with a specific extension using 'Directory.getFiles' and save them in a list

I'm doing a console project whose goal is to search the entire disk for all files with the extension '.config'
I've tried something like:
foreach (string file in Directory.GetFiles("C:\\", "*.config", SearchOption.AllDirectories))
{
Console.WriteLine(file);
Console.ReadLine();
}
but gave me an error "denied access to path (...)".
On the internet I found this code:
Stack<string> pending = new Stack<string>();
pending.Push("C:\\");
while (pending.Count != 0)
{
var path = pending.Pop();
string[] next = null;
try
{
next = Directory.GetFiles(path, "*.config");
}
catch { }
if (next != null && next.Length != 0)
foreach (var file in next)
{
Console.WriteLine(file);
Console.ReadLine();
}
try
{
next = Directory.GetDirectories(path);
foreach (var subdir in next) pending.Push(subdir);
}
catch { }
}
but it just shows the path clicking always in 'enter' and I want to save those files/path in a list.
Someone can help?
There are two things you can do to improve that code:
Use Directory.EnumerateFiles() and Directory.EnumerateDirectories() to avoid making a copy of the names of all the files in each directory.
Make the return type of the method IEnumerable<string> to make it easier to consume.
We also need to be very careful about exceptions caused by attempting to access protected files and directories. The code below is also complicated by the fact that you're not allowed to yield return from inside a try/catch block, so we have to rearrange the code somewhat.
(Also note that we have to dispose the enumerator returned from .GetEnumerator(); normally this is done automatically when you use foreach, but in this case we can't - because of having to avoid doing yield return in a try/catch - so we have to use using to dispose it.)
Here's a modification of your original code to do this:
public static IEnumerable<string> GetFiles(string root, string spec)
{
var pending = new Stack<string>(new []{root});
while (pending.Count > 0)
{
var path = pending.Pop();
IEnumerator<string> fileIterator = null;
try
{
fileIterator = Directory.EnumerateFiles(path, spec).GetEnumerator();
}
catch {}
if (fileIterator != null)
{
using (fileIterator)
{
while (true)
{
try
{
if (!fileIterator.MoveNext()) // Throws if file is not accessible.
break;
}
catch { break; }
yield return fileIterator.Current;
}
}
}
IEnumerator<string> dirIterator = null;
try
{
dirIterator = Directory.EnumerateDirectories(path).GetEnumerator();
}
catch {}
if (dirIterator != null)
{
using (dirIterator)
{
while (true)
{
try
{
if (!dirIterator.MoveNext()) // Throws if directory is not accessible.
break;
}
catch { break; }
pending.Push(dirIterator.Current);
}
}
}
}
}
As an example, here's how you could use a console app to list all the accessible ".txt" files on the "C:\" drive:
static void Main()
{
foreach (var file in GetFiles("C:\\", "*.txt"))
{
Console.WriteLine(file);
}
}
Replace the lines
Console.WriteLine(file);
Console.ReadLine();
with a method to store them in a list.
For example
foundFiles.Add(file);
Then when the method is done, you can read all found file paths from this list.
Notes:
This will not yield all files on the system that match the filter.
Only files where your application has access to their respective directory are found this way.
For example the Windows directory and user directories of other users are usually protected. (assuming you run on Windows)
Keep in mind, that some files might be protected independently of their directory.
So when trying to read them, also consider the fact, that the read might fail.
Just encompass the read with a try catch.
Regarding the error "denied access to path (...)", sometimes you have to run Visual Studio as an a administrator in order to access some folders in the C:\ drive.

Display message to user that a file cannot be loaded to program c#

I am new to working with System.IO.
I have an application that grabs a Json file from the web and only grabs partial data to display on the Windows Application Form's controls.
The form allows the user to save the data as a new file and load a file if ONLY it contains the "indicator" I added in when the file was saved that tells the program it was saved by my program.
Everything works.
Whenever a file that doesn't contain that indicator is loaded to the program, it doesn't show nothing which is what I want it to do, but I also want a Messagebox.Show() to pop up and let the user know why the values are empty and why nothing happened.
if(openFile.ShowDialog() == DialogResult.OK)
{
string dataLine = string.Empty;
using (StreamReader read = new StreamReader(File.OpenRead(openFile.FileName)))
{
dataLine = read.ReadToEnd();
string[] beginningOfData = dataLine.Split(',');
string temporary = string.Empty;
foreach (string line in beginningOfData)
{
//Indicator
if(line.Contains("Indicator")
{
temporary = line.substring(9);
//Goes through the rest of the file
//Converts data to control value and adds it
}
else
{
//Where I tried to display the message box
}
}
}
}
This what I've tried, but it wasn't working as I wanted it too
else
{
MessageBox.Show("Can't load data.");
}
It would still show the MessageBox even if it read that the indicator was there and displayed the data inside the corresponding controls. Also whenever I tried to close the MessageBox it would just show it again.
So I decided to this instead:
else if(!line.contains("Indicator"))
{
MessageBox.Show("Can't load data.");
break;
}
and in this way too:
else
{
if(!line.contains("Indicator"))
{
MessageBox.Show("Can't load data.");
break;
}
}
I also tried making it more specific by doing
if(line.contains("Indicator") == false)
{
//Code here
}
But it would still display it even if the file was created by the program.
The break; did stop the message box from re-appearing again, but I only want the MessageBox to show when it's the incorrect text file (not containing the indicator) and allow me to close the MessageBox to try again.
You can wrap the foreach into an if statement which will use some LINQ code to determine if all lines contain "indicator":
if (beginningOfData.All(line => line.ToLower().Contains("indicator")))
{
string temporary = string.Empty;
foreach (string line in beginningOfData)
{
temporary = line.Substring(9);
//Goes through the rest of the file
//Converts data to control value and adds it
}
}
else
{
System.Windows.Forms.MessageBox.Show("Can't load data.");
}
Contains is case-sensitive. Try this for your evaluation:
line.IndexOf("Indicator", StringComparison.InvariantCultureIgnoreCase) >= 0
I did this instead and it worked for my application
if(openFile.ShowDialog() == DialogResult.OK)
{
string dataLine = string.Empty;
using (StreamReader read = new StreamReader(File.OpenRead(openFile.FileName)))
{
//Changed happened here
dataLine = read.ReadToEnd();
string[] beginningOfData = dataLine.Split(',');
string temporary = string.Empty;
if(beginningOfData.Contains("Indicator"))
{
temporary = dataLine.Substring(9);
foreach(string realData in beginningOfData)
{
//Goes through file
}
}
else
{
MessageBox.Show("Can't load data");
}
}
}

Method seems to be stopping data saving to text file

I have a program where the user can add products to the system, and then search them by the product name.
Everything is working fine, except at the moment is able to enter two products with the same name. I need the program to not allow this.
I have a method assigned to the 'Add' button, which saves the product name, customer name and firmware location to a text file. Here is that method:
private void button_Click(object sender, RoutedEventArgs e)
{
bool found = false;
string searchTerm = productNameTextBox.Text.ToUpper();
if ((productNameTextBox.Text == "") || (customerNameTextBox.Text == "") || (firmwareLocationTextBox.Text == ""))
{
MessageBox.Show("Please fill in all the text boxes");
}
else if (Contains(searchTerm) == true)
{
MessageBox.Show("Product already added");
}
else
{
string inputCustomerName = customerNameTextBox.Text.ToUpper();
string inputProductName = productNameTextBox.Text.ToUpper();
string inputFirmwareLocation = firmwareLocationTextBox.Text;
try
{
Product newProduct = new Product(inputProductName, inputCustomerName, inputFirmwareLocation);
newProduct.Save("Products.txt");
File.AppendAllText("ProductNames.txt", inputProductName + Environment.NewLine);
MessageBox.Show("Product added");
emptyTheTextBoxes();
}
catch
{
MessageBox.Show("Product could not be added");
}
}
}
I have also made a method which will search a text file to see if the users product name has already been stored, and then return a Boolean. This is the method:
public bool Contains (string searchTerm)
{
string line;
bool found = false;
System.IO.StreamReader file = new System.IO.StreamReader("ProductNames.txt");
while ((line = file.ReadLine()) != null)
{
if (line.Contains(searchTerm))
{
found = true;
break;
}
}
if (found == true)
{
return true;
}
else
{
return false;
}
file.Close();
}
When I try to save the input, a message box appears saying "Product could not be added". However, if I comment out the else if statement which calls the method, it works fine.
I thought it may be because I open the file when the method is called, and maybe it wasn't closing properly. So I added the 'file.Close()' and it hasn't made a difference.
I feel like I've just made a silly mistake somewhere, but its been bugging me for hours! Definitely appreciate a fresh pair of eyes!
Thanks
Lucy
In general I would suggest that you separate out your persistence of objects from your object/data management.
You are trying to read and write to the filesystem for the same file in different parts of the program and it seems like you are having an issue with the file not being released, probably because you didn't close it correctly.
You are trying to treat the file system as if it is a database, and this is probably not the best approach. There are of course use cases where this might be needed.
Instead I would suggest the following approach.
During start up read the file. Load the products into a collection
which you keep in memory.
Allow your program to read, update, create, delete products in the
collection.
During shutdown (can also be triggered manually if you want), save
your products onto disk.
This will allow you to avoid such issues. And also be quicker.
Optionally you could also then use something like HashSet<T> for your collection. This does not allow duplicate entries (remember to override equals and hashcode in your Product object). Then when trying to add to the collection if it returns false then it was not added, which would indicate a duplicate. So this might make it easier and quicker to check for you.
I had the file.close() in the wrong place. Here is where I moved it to:
public bool Contains (string searchTerm)
{
string line;
bool found = false;
System.IO.StreamReader file = new System.IO.StreamReader("ProductNames.txt");
while ((line = file.ReadLine()) != null)
{
if (line.Contains(searchTerm))
{
found = true;
break;
}
}
file.Close();
if (found == true)
{
return true;
}
else
{
return false;
}
}

Putting each line from a text file into an array C#

I am working on selecting a text file with a folder pathway via a Windows form in C# and gathering information on each pathway. At the minute, I can import the file and display only the second pathway in the text file, but no information on the folder. Here is the code I have:
private void btnFilePath_Click(object sender, EventArgs e)
{
//creating a stream and setting its value to null
Stream myStream = null;
//allowing the user select the file by searching for it
OpenFileDialog open = new OpenFileDialog();
open.InitialDirectory = "c:\\";
open.Filter = "txt files (*.txt)|*.txt";
open.FilterIndex = 2;
open.RestoreDirectory = true;
//if statement to print the contents of the file to the text box
if (open.ShowDialog() == DialogResult.OK)
{
try
{
if ((myStream = open.OpenFile()) != null)
{
using (myStream)
{
txtFilePath.Text = string.Format("{0}", open.FileName);
if (txtFilePath.Text != "")
{
lstFileContents.Text = System.IO.File.ReadAllText(txtFilePath.Text);
//counting the lines in the text file
using (var input = File.OpenText(txtFilePath.Text))
{
while (input.ReadLine() != null)
{
//getting the info
lstFileContents.Items.Add("" + pathway);
pathway = input.ReadLine();
getSize();
getFiles();
getFolders();
getInfo();
result++;
}
MessageBox.Show("The number of lines is: " + result, "");
lstFileContents.Items.Add(result);
}
}
else
{
//display a message box if there is no address
MessageBox.Show("Enter a valid address.", "Not a valid address.");
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read the file from disk. Original error: " + ex.Message);
}
}
}
I was thinking that copying each line to a variable using a foreach or putting each line into an array and looping through it to gather the information.
Can anyone advise me which would be most suitable so I can go to MSDN and learn for myself, because, I'd prefer to learn it instead of being given the code.
Thanks!
I am not sure what your question is since you seemed to have answered it. If you want us to review it you question would be better suited to Code Review: https://codereview.stackexchange.com/
If you want to use MSDN look here: http://msdn.microsoft.com/en-us/library/System.IO.File_methods(v=vs.110).aspx
Spoiler alert, here is how I would do it:
string[] lines = null;
try
{
lines = File.ReadAllLines(path);
}
catch(Exception ex)
{
// inform user or log depending on your usage scenario
}
if(lines != null)
{
// do something with lines
}
to just gather all lines into array i would use
var lines = File.ReadAllLines(path);
If you want to have more reference rather than the answer itself, take these links one by one all of them explaining things in different manner.
C# File.ReadLines
How to: Read From a Text File (C# Programming Guide)
How to: Read a Text File One Line at a Time (Visual C#)
Hope it will help you to learn more about File IO operations in C#.

Categories