How can I read line by line for a list of files?
I have a directory with a number of files, I need to save all the files in a list and process them one by one, line by line.
So far I have done the following :
//fetching all files from directory
DirectoryInfo d = new DirectoryInfo("path");
Dictionary<int, FileInfo> DatFiles = new Dictionary<int, FileInfo>();
int filecounter = 1;
foreach (var dat in d.EnumerateFiles())
{
DatFiles.Add(filecounter, dat);
filecounter++;
}
Console.WriteLine(filecounter);
foreach (var fileName in DatFiles)
{
foreach (var line in File.ReadLines(fileName.Value.OpenText().ToString()))
{
//run some methods
}
}
When executing, I'm getting an exception, file not found. Even though the list is full of file names.
EnumerateFiles() returns a list of FileInfo objects, and File.ReadLines() takes a string path argument; you probably want to use File.ReadLines(fileName.Value.FullName) in your foreach as that gives the path to the actual file; OpenText() returns a StreamReader object.
This smaller code should do the task
var files = Directory.GetFiles("path");
foreach(var f in files)
{
using(StreamReader reader = new StreamReader(f))
{
var lines = reader.ReadToEnd().Split(new string[]{Environment.NewLine});
// Do here
}
}
When all else fails refer to the documentation
// Open the stream and read it back.
using (StreamReader sr = fi.OpenText())
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
Console.WriteLine(s);
}
}
Related
I am making an app in C# where I am searching if the file exists in the text file or not. If it does not exist then, it would add it in the text file and then append it in a List. But, for some reason the list only takes one file and ends at that point. So, can someone help me with what is the problem in this foreach loop?
static void CheckNewFile()
{
string path_f = #"File_Address_where_Text_file_exists";
var new_file = new List<string>();
if (!File.Exists(path_f)) # Checking if the text file exists or not and then creating it
{
var myFile = File.Create(path_f);
myFile.Close();
}
DirectoryInfo hdDirectoryInWhichToSearch = new DirectoryInfo(#"File_Address_in_which_Files_need_to_be_searched");
FileInfo[] filesInDir = hdDirectoryInWhichToSearch.GetFiles("AC" + "*" + "*.*" + "AC"); # Format of the file to be searched
foreach (FileInfo foundFile in filesInDir) # foreach for the files in the directory
{
string fullName = foundFile.FullName;
int flag = 0;
var lines = File.ReadLines(path_f);
foreach (var line in lines) # Reading line by line and checking if the file exists in the text file before
{
if (String.Equals(line, fullName))
{
flag += 1;
break;
}
}
if (flag < 1)
{
if (new FileInfo(path_f).Length == 0) # File Address is appended in the File
{
//TextWriter tw = new StreamWriter(path_f);
//tw.WriteLine(fullName);
//tw.Close();
}
else
{
//using (var tw = new StreamWriter(path_f, true))
//{
// tw.WriteLine(fullName);
//}
}
new_file.Add(fullName.ToString()); # Adding File Address to the list
flag = 0;
break;
}
}
}
Remove the last break. It is causing the program flow to leave the enclosing foreach loop with the file names.
As the other poster mentioned, you're breaking out of your loop early in the if block.
However, there isn't really a need for the flag (or loop or if block) at all. Your method could be simplified greatly by using a little System.Linq and just using Directory to find the new files by comparing their paths to the contents of the input file.
For example:
static List<string> CheckForNewFiles(string filePath, string searchDir,
string searchPattern)
{
// Create file if it doesn't exist
if (!File.Exists(filePath)) using (File.Create(filePath)) ;
// Get list of files that match search pattern which aren't contained in our file
var newFiles = Directory
.GetFiles(searchDir, searchPattern)
.Where(match => !File.ReadLines(filePath).Contains(match))
.ToList();
// Add the new file paths to our file
File.AppendAllLines(filePath, newFiles);
// Return the list of new files (?)
return newFiles;
}
In use it migth look something like:
public static void Main()
{
Console.WriteLine("Checking for new files...");
var newFiles = CheckForNewFiles(#"c:\temp\paths.txt", #"c:\temp\temp", "*.png");
Console.WriteLine($"{newFiles.Count} files found since last search.");
if (newFiles.Any())
{
Console.WriteLine(" -> " + string.Join(Environment.NewLine + " -> ", newFiles));
}
Console.ReadLine();
}
I want to count the number of some strings and store it into a csv file. I've tried it but I don't know if this is the correct way and in addition, there are two problems.
First of all, here is my method:
public void CountMacNames(String macName)
{
string path = #"D:\Counter\macNameCounter.csv";
if (!File.Exists(path))
{
File.Create(path).Close();
}
var lines = File.ReadLines(path);
foreach (var line in lines)
{
bool isExists = line.Split(',').Any(x => x == macName);
if (isExists)
{
// macName exists, increment it's value by 1
}
else
{
// macName does not exists, add macName to CSV file and start counter by 1
var csv = new StringBuilder();
var newLine = string.Format("{0},{1}", macName, 1);
csv.AppendLine(newLine);
File.WriteAllText(path, csv.ToString());
}
}
}
The first problem is this IOException:
The process cannot access the file 'D:\Counter\macNameCounter.csv'
because it is being used by another process.
The second problem is, that I don't know how to increment the value by one, if a macName exists in the csv file (see first comment)
EDIT: Example for method "CountMacNames" call:
CountMacNames("Cansas");
CountMacNames("Wellback");
CountMacNames("Newton");
CountMacNames("Cansas");
CountMacNames("Princet");
Then, the CSV file should contain:
Cansas, 2
Wellback, 1
Newton, 1
Princet, 1
OK, this is what I'd do:
public void CountMacNames(String macName)
{
string path = #"D:\Counter\macNameCounter.csv";
// Read all lines, but only if file exists
string[] lines = new string[0];
if (File.Exists(path))
lines = File.ReadAllLines(path);
// This is the new CSV file
StringBuilder newLines = new StringBuilder();
bool macAdded = false;
foreach (var line in lines)
{
string[] parts = line.Split(',');
if (parts.Length == 2 && parts[0].Equals(macName))
{
int newCounter = Convert.ToIn32(parts[1])++;
newLines.AppendLine(String.Format("{0},{1}", macName, newCounter));
macAdded = true;
}
else
{
newLines.AppendLine(line.Trim());
}
}
if (!macAdded)
{
newLines.AppendLine(String.Format("{0},{1}", macName, 1));
}
File.WriteAllText(path, newLines.ToString());
}
This code does this:
Read all the lines from file only if it exists - otherwise we start a new file
Iterate over all the lines
If the first part of a 2-part line equals the mac, add 1 to counter and add line to output
If the first part doesn't match or the line format is wrong, add the line to output as is
If we didn't find the mac in any line, add a new line for the mac with counter 1
Write the file back
You can't read and write to the same file at the same time (in a simple way).
For small files, there are already answers.
If your file is really large (too big to fit in memory) you need another approach:
Read input file line by line
optinally modify the current line
write line to a temporary file
If finished delete input file, rename temporary file
For the first problem you can either read all the lines into memory and work there then write it all out again, or use streams.
using (FileStream fs = File.Open(filePath, FileMode.Create, FileAccess.ReadWrite))
{
var sw = new StreamWriter(fs);
var sr = new StreamReader(fs);
while(!streamReader.EndOfStream)
{
var line = sr.ReadLine();
//Do stuff with line.
//...
if (macExists)
{
//Increment the number, Note that in here we can only replace characters,
//We can't insert extra characters unless we rewrite the rest of the file
//Probably more hassle than it's worth but
//You could have a fixed number of characters like 000001 or 1
//Read the number as a string,
//Int.Parse to get the number
//Increment it
//work out the number of bytes in the line.
//get the stream position
//seek back to the beginning of the line
//Overwrite the whole line with the same number of bytes.
}
else
{
//Append a line, also harder to do with streams like this.
//Store the current position,
//Seek to the end of the file,
//WriteLine
//Seek back again.
}
}
}
You need to read the file in and release it, like this, to avoid the IO exception:
string[] lines = null;
using (var sr = new System.IO.StreamReader(path))
lines = sr.ReadToEnd().Split(new string[] {"\r", "\n"}, StringSplitOptions.RemoveEmptyEntries);
As for the count, you can just add an int value, change the method return type as int, too.
public int CountMacNames(String macName, String path)
{
if (!File.Exists(path))
{
File.Create(path).Close();
}
string[] lines = null;
using (var sr = new System.IO.StreamReader(path))
lines = sr.ReadToEnd().Split(new string[] {"\r", "\n"}, StringSplitOptions.RemoveEmptyEntries);
return lines.Where(p => p.Split(',').Contains(macName)).Count();
}
and inside the method that calls it:
var path = #"<PATH TO FILE>";
var cnt = CountMacNames("Canvas", path);
if (cnt > 0)
{
using (var sw = new StreamWriter(path, true, Encoding.Unicode))
sw.WriteLine(string.Format("Canvas,{0}", cnt));
}
Now, var res = CountMacNames("Canvas","PATH"); will return 2, and the lines "Canvas,2" or "Newton,1" will be appended to the file, without overwriting it.
I am trying to create new files with the names created by data from lines in several files.
I want to read all lines of just the .txt files in a folder and then create files from that list.
The files are have various amounts of lines and there could be a various amount of .txt files.
Here is the code that I have tried;
static void WriteToFile(string directory, string name)
{
string filename = String.Format(name);
string path = Path.Combine(directory, filename);
using (StreamWriter sw = File.CreateText(path))
{
sw.WriteLine("Inserted Text");
}
}
......
string[] allFiles = Directory.GetFiles(folder, "*.txt");
foreach (string file in allFiles)
{
string[] lines = File.ReadAllLines(file);
foreach (string newfile in lines)
{
WriteToFile(folder, newfile);
}
}
This is now creating the files but I need to specify the extension as they are just created as 'file1' instead of 'file1.ext'
File.Create Method (String) returns FileStream. The FileStream must be disposed to flush file. Try using this code to create an empty file:
File.Create(fileName).Dispose();
Also, take a look at the similar question: Creating an empty file in C#.
Without any details of error/exception, to me it seems either permission issue, or character issue.
You can use Path.GetInvalidPathChars with Enumerable.Intersect to check if file path contains invalid characters before creating new file
char[] invalidPathChars = Path.GetInvalidPathChars();
foreach (string newfile in lines)
{
if (invalidPathChars.Intersect(newfile).Any())
{
Console.WriteLine(newfile + " contains invalid characters for file name");
continue;
}
File.Create(newfile);
}
File.Create returns a filestream that you can use to write to the new file. But you are not providing the path that is the argument in Create but the line. The line might be an invalid path.
You can use this query instead:
string invalidCharacters = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
var allPaths = Directory.EnumerateFiles(folder, "*.txt")
.SelectMany(path => File.ReadLines(path))
.Select(line => {
string fileName = line;
foreach (Char c in invalidCharacters)
fileName = fileName.Replace(c.ToString(), "_");
return new{ line, path = Path.Combine(targetFolder, fileName) };
} );
foreach(var p in allPaths)
{
File.WriteAllText(p.path, p.line);
}
Edit: however, that will very likely result in this error:
The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the
directory name must be less than 248 characters.
So you need to use a method that shortens the path (f.e. using a logic that appends a number to avoid duplicate names).
static void WriteToFile(string directory, string name)
{
string filename = String.Format(name + ".ini");
string path = Path.Combine(directory, filename);
using (StreamWriter sw = File.CreateText(path))
{
sw.WriteLine("Inserted Text");
}
}
......
var allFiles = Directory.GetFiles(folder, "*.txt");
foreach (string file in allFiles)
{
var lines = File.ReadAllLines(file);
foreach (string newfile in lines)
{
WriteToFile(folder, newfile);
}
}
I am trying to read a list in text file and move the files source to target directory. But as I am trying to read this file using for statement it's giving me following error :
foreach statement cannot operate on variables of type
System.IO.StreamReader because System.IO.StreamReader does not
contain a public definition for GetEnumerator
Not sure if this is issue with file creation or some alternate approach is needed here to read and then move files.
Please advise.
Here's the code:
static void Main(string[] args)
{
create_source_fileList();
string source_dir = System.Configuration.ConfigurationSettings.AppSettings["SourceDir"];
string target_dir = System.Configuration.ConfigurationSettings.AppSettings["TargetDir"];
string dpath = target_dir + "Diff" + ".txt";
TextWriter df = new StreamWriter(dpath);
DirectoryInfo sourceinfo = new DirectoryInfo(source_dir);
DirectoryInfo targetinfo = new DirectoryInfo(target_dir);
string[] source_f_list = File.ReadAllLines(target_dir + "Source_File_List.txt");
string[] target_f_list = File.ReadAllLines(target_dir + "Target_File_List.txt");
IEnumerable<String> file_list_diff = source_f_list.Except(target_f_list);
string diff_list = string.Join(Environment.NewLine, file_list_diff);
df.WriteLine(string.Join(Environment.NewLine, file_list_diff));
df.Close();
System.IO.StreamReader file_read = new System.IO.StreamReader(target_dir + "\\Diff.txt");
if (!Directory.Exists(targetinfo.FullName))
{
Directory.CreateDirectory(targetinfo.FullName);
}
/*foreach (FileInfo fi in sourceinfo.GetFiles())
{
fi.CopyTo(Path.Combine(targetinfo.ToString(), fi.Name), true);
}*/
foreach (string file in file_read) // Error With For Loop
{
}
create_target_fileList();
}
Yes, you can't use foreach directly with StreamReader. It doesn't have the required members for foreach to work.
Options:
Use File.ReadLines instead:
string path = Path.Combine(targetDir, "Diff.txt");
foreach (string file in File.ReadLines(path))
{
// ...
}
Repeatedly call ReadLine on your StreamReader, which should be in a using statement and is simply obtained using File.OpenText:
string path = Path.Combine(targetDir, "Diff.txt");
using (TextReader reader = File.OpenText(path))
{
string file;
while ((file = reader.ReadLine()) != null)
{
// ...
}
}
Note that I've made the variable names a bit cleaner too - idiomatic variable names in C# are things like sourceFileList rather than source_f_list.
Additionally, I'd encourage you to use File.WriteAllText earlier on, rather than the way you're using df (without a using statement, and opening it much earlier than you need to).
Is it possible to read files from a directory one file after another?
I look for something like:
while (File file = Directory.GetFile(path)) {
//
// Do something with file
//
}
[UPDATE]
I already knew about GetFiles() but I'm looking for a function that return one file at a time.
EnumerateFiles() is .Net4.x, would be nice to have, but I'm using .Net2.0. Sorry, that I didn't mention.
(Tag updated)
You can enumerate over the file names:
foreach(string fileName in Directory.EnumerateFiles(path)) {
// Do something with fileName - using `FileInfo` or `File`
}
string[] arFiles = Directory.GetFiles(#"C:\");
foreach (var sFilename in arfiles)
{
// Open file named sFilename for reading, do whatever
using (StreamReader sr = File.OpenText(sFilename ))
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
Console.WriteLine(s);
}
}
}
foreach (var file in Directory.EnumerateFiles(path))
{
var currFileText = File.ReadAllText(file);
}
What about Directory.GetFiles(path) method?
foreach(String fileName in Directory.GetFiles(path))
{
FileInfo file = new FileInfo(fileName);
}
Try with this...
foreach (var filePath in Directory.GetFiles(path))
{
var text = File.ReadAllText(filePath);
// Further processing
}