How to Search if a File Name exists in a text document? - c#

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();
}

Related

How to read and update multiple files

I have 10 txt files in Debug\Tests\Text\ (10 txt files). I need to write a program to open all 10 files and updated every single file. I'm not sure how to do it. Now, I'm actually reading the folder and getting the file name and storing the file name in an array. Below is my code:
private void getFilesName()
{
string[] fileArray = Directory.GetFiles(#"Tests\Text");
//looping through the folder and get the fileNames
for (int i = 0; i<fileArray.Length; i++)
{
MessageBox.Show(fileArray[i]); // I'm doing this is to double check i manage to get the file name.
}
}
After doing this, it do read all the text file name, but the challenge now is for me to access the filename and updating every file in it. I have also created another method just for updating the values in the txt files, below is the code:
private bool modifySQLFile()
{
string destFileName = #"Tests\Text\" // I need the fileName?
string[] fileTexts = File.ReadAllLines(destFileName);
int counter = 0;
//Processing the File
foreach(string line in fileTexts)
{
//only read those non-comments line
if(line.StartsWith("--") == false)
{
//Start to replace instances of Access ID
if(line.Contains(Variable) == true)
{
fileTexts[counter] = fileTexts[counter].Replace(Variable, textBox2.Text);
}
}
counter++;
}
//check if file exists in the backup folder
if(File.Exists("Tests\\Text\\file name "+ textBox1.Text +".sql") == true)
{
MessageBox.Show("This file already exist in the backup folder");
return false;
}
else
{
//update the file
File.WriteAllLines(destFileName, fileTexts);
File.Move(destFileName, "Tests\\Text\\file name"+ textBox1.Text +".sql");
MessageBox.Show("Completed");
return true;
}
}
Your problem seems to be passing the filename variable from the loop to the method.
In order to do what you want, add a parameter to the method:
private bool ModifySQLFile(string filename)
{
string[] fileTexts = File.ReadAllLines(filename);
// ...
}
Then call the method with this parameter:
for (int i = 0; i<fileArray.Length; i++)
{
ModifySQLFile(fileArray[i]);
}
But in general you really don't want to treat a formal language as plaintext like you do. It's very easy to break the SQL like that. What if the user wanted to replace the text "insert", or replaces something with "foo'bar"?
First, implement one (file) modification:
private bool modifySQLFile(String file) {
// given source file, let´s elaborate target file name
String targetFile = Path.Combine(
Path.GetDirectoryName(file),
String.Format("{0}{1}.sql",
Path.GetFileNameWithoutExtension(file),
textBox1.Text));
// In case you want a back up
//TODO: given source file name, elaborate back up file name
//String backUpFile = Path.Combine(...);
// Check (validate) before processing: do not override existing files
if (File.Exists(targetFile))
return false;
//TODO: what if back up file exists? Should we override it? skip?
// if line doesn't start with SQL commentary --
// and contains a variable, substitute the variable with its value
var target = File
.ReadLines(file)
.Select(line => (!line.StartsWith("--") && line.Contains(Variable))
? line.Replace(Variable, textBox2.Text)
: line);
// write modified above lines into file
File.WriteAllLines(targetFile, target);
// In case you want a back up
// Move file to backup
//File.Move(file, backUpFile);
return true;
}
Then call it in the loop:
// enumerate all the text files in the directory
var files = Directory
.EnumerateFiles("#"Tests\Text", "*.txt");
//TODO: you may want filter out some files with .Where
//.Where(file => ...);
// update all the files found above
foreach (var file in files) {
if (!modifySQLFile(file))
MessageBox.Show(String.Format("{0} already exist in the backup folder", file));
}
Please, do not do:
Use Magic values: what is #"Tests\Text\" within your modifySQLFile
Mix UI MessageBox.Show(...) and logic: modifySQLFile returns true or false and it's caller who can display message box.
Materialize when it's not required (Directory.GetFiles, File.ReadAllLines)
If you would like to edit the files in parallel. With threads you can parallelize work.
for (int i = 0; i < fileArray.Length; i++)
new Thread(UpdateFileThread).Start(fileArray[i]);
private void UpdateFileThread(object path)
{
string filePath = (string)path;
//ToDo: Edit file
}
In your case you would create 10 Threads. That solution works, but is a bad pattern if you have to deal with more than 10 files.
Below i have posted the real time code ,which i have used project
protected void btnSqlfinder_Click(object sender, EventArgs e)
{
//Defining the path of directory where all files saved
string filepath = # "D:\TPMS\App_Code\";
//get the all file names inside the directory
string[] files = Directory.GetFiles(filepath);
//loop through the files to search file one by one
for (int i = 0; i < files.Length; i++)
{
string sourcefilename = files[i];
StreamReader sr = File.OpenText(sourcefilename);
string sourceline = "";
int lineno = 0;
while ((sourceline = sr.ReadLine()) != null)
{
lineno++;
//defining the Keyword for search
if (sourceline.Contains("from"))
{
//append the result to multiline text box
TxtResult.Text += sourcefilename + lineno.ToString() + sourceline + System.Environment.NewLine;
}
if (sourceline.Contains("into"))
{
TxtResult.Text += sourcefilename + lineno.ToString() + sourceline + System.Environment.NewLine;
}
if (sourceline.Contains("set"))
{
TxtResult.Text += sourcefilename + lineno.ToString() + sourceline + System.Environment.NewLine;
}
if (sourceline.Contains("delete"))
{
TxtResult.Text += sourcefilename + lineno.ToString() + sourceline + System.Environment.NewLine;
}
}
}
}
This code will fetch the multiple files in the given directory,and show the lines as per the keyword in a separate text.
But you can easily change as per your requirement,Kindly let me know your thoughts.
Thanks

Only write data to text file if the line doesnt match any others

I want to add data into a text file based on a specific output. It will read an XML file and write a certain line to a text file. If the data is already written into the text file, I don't want to write it again.
Code
public void output(string folder)
{
string S = "Data" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xml";
//Trades.Save(S);
string path = Path.Combine(folder, S);
Console.WriteLine(path);
XDocument f = new XDocument(Trades);
f.Save(path);
string[] lines = File.ReadAllLines(path);
File.WriteAllLines(path, lines);
using (System.IO.StreamWriter file = new System.IO.StreamWriter(
#"H:\Test" + DateTime.Now.ToString("yyMMdd") + ".txt", true))
{
foreach (string line in lines)
{
if (line.Contains("CertainData"))
{
file.WriteLine(line);
if (File.ReadAllLines(path).Any(x => x.Equals(line)))
{
}
else
{
string[] tradeRefLines = File.ReadAllLines(path);
File.WriteAllLines(path, tradeRefLines); ;
}
}
}
}
}
The problem is it will still write the line even if the data is exactly the same elsewhere. I don't want duplicate lines.
Any advice?
Edit
The CertainData is a reference number.
I have a bunch of files that have data in them and the piece I want to seperate and put into a text file is CertainData field, which will have a reference number.
Sometimes the files I get sent will have the same formatted information inside it with the CertainData appearing in them for reference.
When i run this programme, if the text file i have already contains the "CertainData" reference number inside it, i dont want it to be written
If you need anymore clarification let me know and i will update the post
Try with this LINQ:
var previousLines = new HashSet<string>();
File.WriteAllLines(destPath, File.ReadLines(sourcePath)
.Where(line => previousLines.Add(line)));
EDITED :
public void output(string folder)
{
string S = "Data" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xml";
//Trades.Save(S);
string path = Path.Combine(folder, S);
Console.WriteLine(path);
XDocument f = new XDocument(Trades);
f.Save(path);
string[] lines = File.ReadAllLines(path);
File.WriteAllLines(path, lines);
bool isExist = false;
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"H:\Test" + DateTime.Now.ToString("yyMMdd") + ".txt", true))
{
foreach (string line in lines)
{
if (line.Contains("CertainData"))
{
isExist = true;
}
}
if (!isExist)
{
File.AppendAllText(path, "CertainData" + Environment.NewLine);
}
}
}

Replace data/value of csv file

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.

Create files from multiple files contents

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);
}
}

read IEnumerable text list and issue move commands C#

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).

Categories

Resources