How to close file that has been read - c#

So im trying to close a file (transactions.txt) that has been open that i've used to read into a textbox and now I want to save back to the file but the problem debug says that the file is in use so I need to find a way to close it. Can anyone help me with this? Thanks!
SearchID = textBox1.Text;
string ID = SearchID.ToString();
bool idFound = false;
int count = 0;
foreach (var line in File.ReadLines("transactions.txt"))
{
//listView1.Items.Add(line);
if (line.Contains(ID))
{
idFound = true;
}
//Displays Transactions if the variable SearchID is found.
if (idFound && count < 8)
{
textBox2.Text += line + "\r\n";
count++;
}
}
}
private void SaveEditedTransaction()
{
SearchID = textBox1.Text;
string ID = SearchID.ToString();
bool idFound = false;
int count = 0;
foreach (var lines in File.ReadLines("transactions.txt"))
{
//listView1.Items.Add(line);
if (lines.Contains(ID))
{
idFound = true;
}
if (idFound)
{
string edited = File.ReadAllText("transactions.txt");
edited = edited.Replace(lines, textBox2.Text);
File.WriteAllText("Transactions.txt", edited);
}

The problem here is that File.ReadLines keeps the file open while you read it, since you've put the call to write new text to it inside the loop, the file is still open.
Instead I would simply break out of the loop when you find the id, and then put the if-statement that writes to the file outside the loop.
This, however, means that you will also need to maintain which line to replace in.
So actually, instead I would switch to using File.ReadAllLines. This reads the entire file into memory, and closes it, before the loop starts.
Now, pragmatic minds might argue that if you have a lot of text in that text file, File.ReadLines (that you're currently using) will use a lot less memory than File.ReadAllLines (that I am suggesting you should use), but if that's the case then you should switch to a database, which would be much more suited to your purpose anyway. It is, however, a bit of an overkill for a toy project with 5 lines in that file.

Use StreamReader directly with the using statement, for example:
var lines = new List<string>();
using (StreamReader reader = new StreamReader(#"C:\test.txt")) {
var line = reader.ReadLine();
while (line != null) {
lines.Add(line);
line = reader.ReadLine();
}
}
By using the using statement the StreamReader instance will automatically be disposed of after it's done with it.

You can try with this:
File.WriteAllLines(
"transactions.txt",
File.ReadAllLines("transactions.txt")
.Select(x => x.Contains(ID) ? textBox2.Text : x));
It works fine, but if the file is big you have to find other solutions.

You can use the StreamReader class instead of the methods of the File class. In this way you can use, Stream.Close() and Stream.Dispose().

Related

c# - splitting a large list into smaller sublists

Fairly new to C# - Sitting here practicing. I have a file with 10 million passwords listed in a single file that I downloaded to practice with.
I want to break the file down to lists of 99. Stop at 99 then do something. Then start where it left off and repeat the do something with the next 99 until it reaches the last item in the file.
I can do the count part well, it is the stop at 99 and continue where I left off is where I am having trouble. Anything I find online is not close to what I am trying to do and anything I add to this code on my own does not work.
I am more than happy to share more information if I am not clear. Just ask and will respond however, I might not be able to respond until tomorrow depending on what time it is.
Here is the code I have started:
using System;
using System.IO;
namespace lists01
{
class Program
{
static void Main(string[] args)
{
int count = 0;
var f1 = #"c:\tmp\10-million-password-list-top-1000000.txt";
{
var content = File.ReadAllLines(f1);
foreach (var v2 in content)
{
count++;
Console.WriteLine(v2 + "\t" + count);
}
}
}
}
}
My end goal is to do this with any list of items from files I have. I am only using this password list because it was sizable and thought it would be good for this exercise.
Thank you
Keith
Here is a couple of different ways to approach this. Normally, I would suggest the ReadAllLines function that you have in your code. The trade off is that you are loading the entire file into memory at once, then you operate on it.
Using read all lines in concert with Linq's Skip() and Take() methods, you can chop the lines up into groups like this:
var lines = File.ReadAllLines(fileName);
int linesAtATime = 99;
for (int i = 0; i < lines.Length; i = i + linesAtATime)
{
List<string> currentLinesGroup = lines.Skip(i).Take(linesAtATime).ToList();
DoSomethingWithLines(currentLinesGroup);
}
But, if you are working with a really large file, it might not be practical to load the entire file into memory. Plus, you might not want to leave the file open while you are working on the lines. This option gives you more control over how you move through the file. It just loads the part it needs into memory, and closes the file while you are working on the current set of lines.
List<string> lines = new List<string>();
int maxLines = 99;
long seekPosition = 0;
bool fileLoaded = false;
string line;
while (!fileLoaded)
{
using (Stream stream = File.Open(fileName, FileMode.Open))
{
//Jump back to the previous position
stream.Seek(seekPosition, SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(stream))
{
while (!reader.EndOfStream && lines.Count < maxLines)
{
line = reader.ReadLine();
seekPosition += (line.Length + 2); //Tracks how much data has been read.
lines.Add(line);
}
fileLoaded = reader.EndOfStream;
}
}
DoSomethingWithLines(lines);
lines.Clear();
}
In this case, I used Stream because it has the ability to seek to a specific position in the file. But then I used StreaReader because it has the ReadLine() methods.

Why is my method adding an extra blank line at the end?

I use the below method to remove all blank lines from a file but it is for some reason adding an extra line at the end of the document?
var tempFileName = Path.GetTempFileName();
try
{
using (var streamReader = new StreamReader(file))
using (var streamWriter = new StreamWriter(tempFileName))
{
string line;
while ((line = streamReader.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
streamWriter.WriteLine(line);
}
}
File.Copy(tempFileName, file, true);
}
finally
{
File.Delete(tempFileName);
}
How do I fix this?
Also can the code be made shorter?
Also can the code be made shorter?
A more efficient solution to the other answer:
File.WriteAllLines(file, File.ReadLines("some/path").Where(l => !string.IsNullOrWhiteSpace(l)));
File.ReadLines() is more efficient than File.ReadAllLines() because it allows you to query the IEnumerable<string> without reading it all into memory first.
We then take the resulting IEnumerable<string> from our Where() method and pass it to an overload of File.WriteAllLines() which takes an IEnumerable<string> as its second parameter.
StreamWriter.WriteLine() will always append a new line (a carriage return, line feed pair) after the string, so I assume that's what you're referring to. To me, it's normal and best to have the last line followed by a new line. But if you don't want it, don't write one.
As far as the length of your code, it looks about right to me. If it seems to detract from your program logic, just move it into its own method.
For efficiency, you might try deleting the original file and then renaming the temporary file. That would be more efficient than copying the entire file.
var tempFileName = Path.GetTempFileName();
try
{
using (var streamReader = new StreamReader(file))
using (var streamWriter = new StreamWriter(tempFileName))
{
string line;
bool isFirstLine = true;
while ((line = streamReader.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
{
if (!isFirstLine)
streamWrite.WriteLine();
streamWriter.Write(line);
isFirstLine = false;
}
}
}
File.Delete(file);
File.Move(tempFileName, file);
}
finally
{
File.Delete(tempFileName);
}
The problem is that StreamReader.ReadLine does not return the line breaks themselves, thus destroying line break information. In other words, those two input files:
File 1: A\r\nB\r\n
File 2: A\r\nB
Will yield the same input to your method. You can't determine that way whether there was a final line break or not.
If you never want a final line break, use Write instead of WriteLine and manually add a line break at the beginning of your loop in every iteration except for the first one:
...
string line;
bool first = true;
while ((line = streamReader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line))
continue;
if (!first)
streamWriter.WriteLine();
streamWriter.Write(line);
first = false;
}
...

How do I read from a file?

I'm trying to get my program to read code from a .txt and then read it back to me, but for some reason, it crashes the program when I compile. Could someone let me know what I'm doing wrong? Thanks! :)
using System;
using System.IO;
public class Hello1
{
public static void Main()
{
string winDir=System.Environment.GetEnvironmentVariable("windir");
StreamReader reader=new StreamReader(winDir + "\\Name.txt");
try {
do {
Console.WriteLine(reader.ReadLine());
}
while(reader.Peek() != -1);
}
catch
{
Console.WriteLine("File is empty");
}
finally
{
reader.Close();
}
Console.ReadLine();
}
}
I don't like your solution for two simple reasons:
1)I don't like gotta Cath 'em all(try catch). For avoing check if the file exist using System.IO.File.Exist("YourPath")
2)Using this code you haven't dispose the streamreader. For avoing this is better use the using constructor like this: using(StreamReader sr=new StreamReader(path)){ //Your code}
Usage example:
string path="filePath";
if (System.IO.File.Exists(path))
using (System.IO.StreamReader sr = new System.IO.StreamReader(path))
{
while (sr.Peek() > -1)
Console.WriteLine(sr.ReadLine());
}
else
Console.WriteLine("The file not exist!");
If your file is located in the same folder as the .exe, all you need to do is StreamReader reader = new StreamReader("File.txt");
Otherwise, where File.txt is, put the full path to the file. Personally, I think it's easier if they are in the same location.
From there, it's as simple as Console.WriteLine(reader.ReadLine());
If you want to read all lines and display all at once, you could do a for loop:
for (int i = 0; i < lineAmount; i++)
{
Console.WriteLine(reader.ReadLine());
}
Use the code below if you want the result as a string instead of an array.
File.ReadAllText(Path.Combine(winDir, "Name.txt"));
Why not use System.IO.File.ReadAllLines(winDir + "\Name.txt")
If all you're trying to do is display this as output in the console, you could do that pretty compactly:
private static string winDir = Environment.GetEnvironmentVariable("windir");
static void Main(string[] args)
{
Console.Write(File.ReadAllText(Path.Combine(winDir, "Name.txt")));
Console.Read();
}
using(var fs = new FileStream(winDir + "\\Name.txt", FileMode.Open, FileAccess.Read))
{
using(var reader = new StreamReader(fs))
{
// your code
}
}
The .NET framework has a variety of ways to read a text file. Each have pros and cons... lets go through two.
The first, is one that many of the other answers are recommending:
String allTxt = File.ReadAllText(Path.Combine(winDir, "Name.txt"));
This will read the entire file into a single String. It will be quick and painless. It comes with a risk though... If the file is large enough, you may run out of memory. Even if you can store the entire thing into memory, it may be large enough that you will have paging, and will make your software run quite slowly. The next option addresses this.
The second solution allows you to work with one line at a time and not load the entire file into memory:
foreach(String line in File.ReadLines(Path.Combine(winDir, "Name.txt")))
// Do Work with the single line.
Console.WriteLine(line);
This solution may take a little longer for files because it's going to do work MORE OFTEN with the contents of the file... however, it will prevent awkward memory errors.
I tend to go with the second solution, but only because I'm paranoid about loading huge Strings into memory.

Append Text with StringReader (After reader.ReadLine())

Right now I'm building a game application using c#, which will require loading from text file for the game script. (It's quite a simple visual novel game)
Now when the main form loads, i load the script from the file script.txt
And i declared :
StringReader reader = new StringReader(script);
as a global variable there
Now in the middle of the game where the reader is in the middle of the string script,
I need to append starting from next line of the reader.
Basically what I want to achieve:
Append all texts from "news.txt" to script starting from reader.ReadLine() [i.e. in the middle of the string script]
What will be the most efficient solution to achieve this?
What I know:
StreamReader sr = new StreamReader("news.txt");
string news = sr.ReadToEnd();
//Now how to append 'news' to reader.ReadLine() ??
Edit for more clarification (sorry, this is my first time asking here) :
I will try to explain more about what i'm trying to achieve here.
What i'm having right now :
//global variables
string script;
StringReader reader;
//during form_load
StreamReader sr = new StreamReader("script.txt");
script = sr.ReadToEnd();
reader - new StringReader(script);
//And as the game progresses, I keep on implementing reader.ReadLine()..
//At one point, the program will ask the user, do you want to watch the news?
DialogResult dialogResult = MessageBox("Do you want to watch the news?", , MessageBoxButtons.YesNo
if(dialogResult == DialogResult.Yes)
{
StreamReader newsSr = new StreamReader("news.txt");
string news = newsSr.ReadToEnd();
//now I want to append the contents of 'news' to the string 'script' after reader.ReadLine() - any best way to implement this?
}
One possible way (and i think it's the worst way as well) is by introducing one count variable, to get the starting position of the last reader.ReadLine(),
and execute the desired result using Insert like follows :
script = script.Insert(startIndex, news)
You cannot write into a StringReader.
However, if I understand your latest question I think you want this.
StreamReader sr = new StreamReader("news.txt");
string news = string.Empty;
string line = sr.ReadLine();
while (line != null)
{
news += line;
news += someOtherString;
line = sr.ReadLine();
}
Aside I wouldn't do this with string concatenation. I would do this with a StringBuilder.
Just load the file into memory using File.ReadAllLines().
You can then access this as a string array, without worrying about readers, writers, streams etc.
For example:
// load files as arrays
string[] scriptLinesArray = File.ReadAllLines("script.txt");
string[] newsLinesArray = File.ReadAllLines("news.txt");
// convert arrays to lists
var script = new List<string>(scriptLinesArray);
var news = new List<string>(newsLinesArray );
// append news list to script list
script.AddRange(news);
in the end I was able to resolve this problem.
This is what I used (in case anyone wanna know :))
//I'm using a switch statement, in case reader.ReadLine() == "#(morningnews)"
dialogResult = MessageBox.Show("Do you want to watch the news?", , MessageBoxButtons.YesNo);
if(dialogResult = DialogResult.Yes)
{
StreamReader sr = new StreamReader(directoryName + "\\morningactivities\\morningnews1.txt");
string news = sr.ReadToEnd();
script = script.Replace("#(morningnews)", "#(morningnews)\n" + news);
reader = new StringReader(script);
while (reader.ReadLine() != "#(morningnews)")
continue;
loadNextScript();
}
Thanks for everyone who helped, it gave me the inspiration to actually came up with this.

How can I split a big text file into smaller file?

I have a big file with some text, and I want to split it into smaller files.
In this example, What I do:
I open a text file let's say with 10 000 lines into it
I set a number of package=300 here, which means, that's the small file limit, once a small file has 300 lines into it, close it, open a new file for writing for example (package2).
Same, as step 2.
You already know
Here is the code from my function that should do that. The ideea (what I dont' know) is how to close, and open a new file once it has reached the 300 limit (in our case here).
Let me show you what I'm talking about:
int nr = 1;
package=textBox1.Text;//how many lines/file (small file)
string packnr = nr.ToString();
string filer=package+"Pack-"+packnr+"+_"+date2+".txt";//name of small file/s
int packtester = 0;
int package= 300;
StreamReader freader = new StreamReader("bigfile.txt");
StreamWriter pak = new StreamWriter(filer);
while ((line = freader.ReadLine()) != null)
{
if (packtester < package)
{
pak.WriteLine(line);//writing line to small file
packtester++;//increasing the lines of small file
}
else if (packtester == package)//in this example, checking if the lines
//written, got to 300
{
packtester = 0;
pak.Close();//closing the file
nr++;//nr++ -> just for file name to be Pack-2;
packnr = nr.ToString();
StreamWriter pak = new StreamWriter(package + "Pack-" + packnr + "+_" + date2 + ".txt");
}
}
I get this errors:
Cannot use local variable 'pak' before it is declared
A local variable named 'pak' cannot be declared in this scope because it would give a different meaning to 'pak', which is already used in a 'parent or current' scope to denote something else
Try this:
public void SplitFile()
{
int nr = 1;
int package = 300;
DateTime date2 = DateTime.Now;
int packtester = 0;
using (var freader = new StreamReader("bigfile.txt"))
{
StreamWriter pak = null;
try
{
pak = new StreamWriter(GetPackFilename(package, nr, date2), false);
string line;
while ((line = freader.ReadLine()) != null)
{
if (packtester < package)
{
pak.WriteLine(line); //writing line to small file
packtester++; //increasing the lines of small file
}
else
{
pak.Flush();
pak.Close(); //closing the file
packtester = 0;
nr++; //nr++ -> just for file name to be Pack-2;
pak = new StreamWriter(GetPackFilename(package, nr, date2), false);
}
}
}
finally
{
if(pak != null)
{
pak.Dispose();
}
}
}
}
private string GetPackFilename(int package, int nr, DateTime date2)
{
return string.Format("{0}Pack-{1}+_{2}.txt", package, nr, date2);
}
Logrotate can do this automatically for you. Years have been put into it and it's what people trust to handle their sometimes very large webserver logs.
Note that the code, as written, will not compile because you define the variable pak more than once. It should otherwise function, though it has some room for improvement.
When working with files, my suggestion and the general norm is to wrap your code in a using block, which is basically syntactic sugar built on top of a finally clause:
using (var stream = File.Open("C:\hi.txt"))
{
//write your code here. When this block is exited, stream will be disposed.
}
Is equivalent to:
try
{
var stream = File.Open(#"C:\hi.txt");
}
finally
{
stream.Dispose();
}
In addition, when working with files, always prefer opening file streams using very specific permissions and modes as opposed to using the more sparse constructors that assume some default options. For example:
var stream = new StreamWriter(File.Open(#"c:\hi.txt", FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read));
This will guarantee, for example, that files should not be overwritten -- instead, we assume that the file we want to open doesn't exist yet.
Oh, and instead of using the check you perform, I suggest using the EndOfStream property of the StreamReader object.
This code looks like it closes the stream and re-opens a new stream when you hit 300 lines. What exactly doesn't work in this code?
One thing you'll want to add is a final close (probably with a check so it doesn't try to close an already closed stream) in case you don't have an even multiple of 300 lines.
EDIT:
Due to your edit I see your problem. You don't need to redeclare pak in the last line of code, simply reinitialize it to another streamwriter.
(I don't remember if that is disposable but if it is you probably should do that before making a new one).
StreamWriter pak = new StreamWriter(package + "Pack-" + packnr + "+_" + date2 + ".txt");
becomes
pak = new StreamWriter(package + "Pack-" + packnr + "+_" + date2 + ".txt");

Categories