Cannot Create a file when using File.Move Error - c#

I have a .csv file as the attached image which has a list of folders and files . I would like to read the .csv file and recreate the same folder structure under different folder.
Say for example I have C:\Data\SourceFolder\Folder2\Folder4\File1.txt , I would like the file to be moved to C:\Data\FilesCopiedfromC\SourceFolder\Folder2\Folder4\File1.txt . In the above destinaton path , the C:\Data\FilesCopiedfromC is going to be the same always . I am able to create the folder structure in the destination but when I do the file.move from source to destination I get a "File cannot be created when it already exists error".
try
{
string inputfile = textBox1.Text.ToString();
using(StreamReader reader = new StreamReader(inputfile))
{
string headerline = reader.ReadLine();
Boolean firstline = true;
string line = string.Empty;
string SourceFileNameCSV;
string SourceFilePathCSV,totalSourceFilePath, strConstructedDestinationfullpath;
string[] parts;
while ((line = reader.ReadLine()) != null)
{
char[] delimiters = new char[] { ',' };
parts= line.Split(delimiters);
if (parts.Length > 0)
{
SourceFilePathCSV = parts[0];
SourceFileNameCSV = parts[1];
totalSourceFilePath = SourceFilePathCSV + "\\" + SourceFileNameCSV;
strDestinationDynamicPath = SourceFilePathCSV.Replace("C:\\Data\\", " ").TrimEnd();
strConstructedDestinationfullpath = Path.Combine(strDestinationStaticPath, strDestinationDynamicPath);
if (!string.IsNullOrEmpty(strConstructedDestinationfullpath))
{
if (!Directory.Exists(strDestinationDynamicPath))
{
Directory.CreateDirectory(strConstructedDestinationfullpath);
}
// File.Move(totalSourceFilePath, strConstructedDestinationfullpath);
}
}
}
}
}//try
Any help is appreciated.

You need to specify a file name for the destination, currently you are just providing a path:
File.Move(
totalSourceFilePath,
Path.Combine(strConstructedDestinationfullpath, Path.GetFileName(totalSourceFilePath));

It's because, apparently, the file already exists in the destination. What you can do is check if the file exists an delete if so:
if (System.IO.File.Exists("filename"))
{
//delete
System.IO.File.Delete("filename"); //try/catch exception handling
needs to be implemented
}

Related

C# Read Files location and destination IN CSV and copy to a different folder

I have a CSV file that will be populated with different file names, source and archive location
And I would like to read the CSV file and copy/ Move each file name to an archive and stamp each file moved with date and time in the archive
my CSV "test.csv" is like this
Number FileName Source Destination**
1, Support.CSv, C:\home, C:\Support\Archive
2, Account.txt, c:\home, D:\Account\Archive
3, Support5.csv, C:\home, C:\Support\Archive
4, allusers.csv, c:\home, D:\Account\Archive
5, Users2.csv, c:\home, D:\Account\Archive
How can I achieve this , I have tried the below code but all the files are copied in the same directory and this is not what I am looking for
string sourceDir1;
string backupDir1;
var path = #"C:/test.csv";
using (TextFieldParser csvReader = new TextFieldParser(path))
{
csvReader.CommentTokens = new string[] { "#" };
csvReader.SetDelimiters(new string[] { "," });
csvReader.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names
csvReader.ReadLine();
while (!csvReader.EndOfData)
{
// Read current line fields, pointer moves to the next line.
string[] fields = csvReader.ReadFields();
String sourceDir = fields[2];
string backupDir = fields[3];
try
{
string[] picList = Directory.GetFiles(sourceDir, "*.csv");
string[] txtList = Directory.GetFiles(sourceDir, "*.txt");
;
// Copy CSV files.
foreach (string f in picList)
{
// Remove path from the file name.
string fName = f.Substring(sourceDir.Length + 1);
// Use the Path.Combine method to safely append the file name to the path.
// Will overwrite if the destination file already exists.
File.Copy(Path.Combine(sourceDir, fName), Path.Combine(backupDir, fName), true);
}
// Copy text files.
foreach (string f in txtList)
{
// Remove path from the file name.
string fName = f.Substring(sourceDir.Length + 1);
try
{
// Will not overwrite if the destination file already exists.
File.Copy(Path.Combine(sourceDir, fName), Path.Combine(backupDir, fName));
}
// Catch exception if the file was already copied.
catch (IOException copyError)
{
Console.WriteLine(copyError.Message);
}
}
There is no need of checking source items on each loop and re-copy items again, you can check if the current file exists and then just copy it to the specific destination. In this case your code will look like this:
var path = #"C:/test.csv";
using (TextFieldParser csvReader = new TextFieldParser(path))
{
csvReader.CommentTokens = new string[] { "#" };
csvReader.SetDelimiters(new string[] { "," });
csvReader.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names
csvReader.ReadLine();
while (!csvReader.EndOfData)
{
// Read current line fields, pointer moves to the next line.
string[] fields = csvReader.ReadFields();
string fileName = fields[1];
string sourceDir = fields[2];
string backupDir = fields[3];
try
{
var sourceFilePath = $#"{sourceDir}\{fileName}";
var sourceFileExists = File.Exists(sourceFilePath);
if (sourceFileExists)
{
//check if the destination directory exists
var destinationDirectory = Directory.Exists($#"{backupDir}");
if (!destinationDirectory)
{
Directory.CreateDirectory($#"{backupDir}");
}
var destinationFilePath = Path.Combine($#"{backupDir}", fileName);
File.Copy(sourceFilePath, destinationFilePath, true);
}
else
{
throw new Exception("File was not found");
}
}
catch(IOException ex)
{
throw new Exception(ex.Message);
}
}
}
You will also have to alter the destination file name by adding the timestamp.

Read config file and create a log file

I have a config file called one_two.config.txt containing the path of a log file to be written.
I want to read this line ( 'comdir=C:\Users\One\Desktop' ) and then create a new log file in a given directory.
The log file is going to have some data ( Time / Date / ID etc. )
Here is what i have right now :
string VarSomeData = ""; // Contains Data that should be written in log.txt
for (Int32 i = 0; i < VarDataCount; i++)
{
csp2.DataPacket aPacket;
VarData = csp2.GetPacket(out aPacket, i, nComPort);
VarSomeData = String.Format("\"{0:ddMMyyyy}\",\"{0:HHmmss}\",\"{1}\",\"{2}\",\"{3}\" \r\n", aPacket.dtTimestamp, VarPersNr, aPacket.strBarData, VarId.TrimStart('0'));
string line = "";
using (StreamReader sr = new StreamReader("one_two.config.txt"))
using (StreamWriter sw = new StreamWriter("log.txt"))
{
while ((line = sr.ReadLine()) != null)
{
if((line.StartsWith("comdir="))
{
// This is wrong , how should i write it ?
sw.WriteLine(VarSomeData);
}
}
}
}
Right now the log file is being created in same directory as the config file.
This should get you started:
string line;
using (StreamReader file = new StreamReader("one_two.config.txt"))
using (StreamWriter newfile = new StreamWriter("log.txt"))
{
while ((line = file.ReadLine()) != null)
{
newfile.WriteLine(line);
}
}
So basically, you have a config file containing the path of a log file to write; but you're not saying anything about the content of that log file. You just want to know where to create it, correct?
Something like
string ConfigPath = "one_two.config.txt";
string LogPath = File.ReadAllLines(ConfigPath).Where(l => l.StartsWith("comdir=")).FirstOrDefault()
if (!String.IsNullOrEmpty(LogPath)) {
using (TextWriter writer = File.CreateText(LogPath.SubString(7))) {
writer.WriteLine("Log file created.");
}
}
You can also read the configuration line this way with a little bit more code but you'll get better performance
string LogPath = null;
using (StreamReader file = new System.IO.StreamReader(ConfigPath)) {
while((line = file.ReadLine()) != null) {
if (line.StartsWith("comdir="))
LogPath = line.Substring(7);
}
}
For the configuration file, you might want to consider using a C# class that you serialize as an XML file and then deserialize when launching the application. Then you have the configuration already available within a class whenever you need it.
//Input file path
string inPath = "C:\\Users\\muthuraman\\Desktop\\one_two.config.txt";
//Output File path
string outPath = "C:\\Users\\muthuraman\\Desktop\\log.txt";
// Below code reads all the lines in the text file and Store the text as array of strings
string[] input=System.IO.File.ReadAllLines(inPath);
//Below code write all the text in string array to the specified output file
System.IO.File.WriteAllLines(outPath, input);

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

Read file with partially unknown filename

I want to read in a text file but I only know a part of the filename. To be more specific, the format of the file is "FOO_yyyymmdd_hhmmss.txt" but when running my program, I will only know "FOO_yyyymmdd_" and ".txt". In other words, I want to read that file based on just the date, ignoring the "hhmmss" (time) part for I will not know the time of that file, only the date.
Here is part of what I have so far:
ArrayList al = new ArrayList();
string FileName = "FOO_" + DateTime.Now.ToString("yyyymmdd") + "_" ; //how do I correct this, keeping in mind that I need the time as well?
string InPath = #"\\myServer1\files\";
string OutPath = #"\\myServer2\files\";
string InFile = InPath + FileName;
string OutFile = OutPath + #"faceOut.txt";
using (StreamReader sr = new StreamReader(InFile))
{
string line;
while((line = sr.ReadLine()) != null)
{
al.Add(line);
}
sr.Close();
}
How can I read this file without knowing the whole string beforehand?
How about using a wildcard * available with DirectoryInfo.EnumerateFiles
string FileName = new DirectoryInfo(#"\\myServer1\files\")
.EnumerateFiles(String.Format("FOO_{0:yyyymmdd}_*.txt", DateTime.Now))
.FirstOrDefault()?.FullName;
FileName == null means that the file was not found
Note that the Null-Conditional Operator (?.) can only be used from C# 6.0 onwards
Well, search for the files, then check that theres's only one file to read:
var pathToSearch = #"\\myServer1\files\";
var knownPart = string.Format("FOO_{0:yyyymmdd}_", DateTime.Now);
var files = Directory
.EnumerateFiles(pathToSearch, knownPart + "??????.txt")
.Where(file => Regex.IsMatch(
Path.GetFileNameWithoutExtension(file).Substring(knownPart.Length),
"^(([0-1][0-9])|(2[0-3]))([0-5][0-9]){2}$"))
.ToArray();
if (files.Length <= 0) {
// No such files are found
// Probably, you want to throw an exception here
}
else if (files.Length > 1) {
// Too many such files are found
// Throw an exception or select the right file from "files"
}
else {
// There's one file only
var fileName = files[0];
...
}

Categories

Resources