I have a method in C# which gets files in a directory this way:
FileInfo[] fileInfo = new DirectoryInfo(mypath).GetFiles();
Some of the files in the directory are not the ones we need to process (the only way to know is by its content, not the file extension) so we would like to remove them from the FileInfo list (not from disk).
I was searching for a simple way to exclude a file in the FileInfo array but there seems not to be a way.
Here's the whole code which checks the files we only need in the directory the user selects:
int number_of_files = fileInfo.Length;
for (int i = 0; i < number_of_files ; ++i)
{
string file= fileInfo[i].FullName;
BinaryReader br = new BinaryReader(new FileStream(file, FileMode.Open, FileAccess.Read), Encoding.ASCII);
byte[] preamble = new byte[132];
br.Read(preamble, 0, 132);
if (preamble[128] != 'D' || preamble[129] != 'I' || preamble[130] != 'C' || preamble[131] != 'M')
{
if (preamble[0] + preamble[1] != 0008)
{
return; //Rather than return, remove the file in question from the list....
}
}
br.Dispose();
}
Any ideas how can I do this?
Instead of removing the file from the FileInfo[] array, consider just creating a separate list that collects all files that you do want to keep:
FileInfo[] files = new DirectoryInfo(mypath).GetFiles();
List<FileInfo> filteredFiles = new List<FileInfo>();
foreach (FileInfo file in fileInfos)
{
string file= fileInfo[i].FullName;
using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read))
using (var br = new BinaryReader(stream, Encoding.ASCII))
{
byte[] preamble = new byte[132];
br.Read(preamble, 0, 132);
if (preamble[128] != 'D' || preamble[129] != 'I' || preamble[130] != 'C' || preamble[131] != 'M')
{
if (preamble[0] + preamble[1] != 0008)
{
// skip this file
continue;
}
// keep the file
filteredFiles.Add(file);
// do something else with the file
}
}
}
You should think about whether reading the files just to filter them is really worth the effor though. If you later end up processing the filtered files too, you should really consider doing that at the same time, so you don’t have to open the file twice (once to figure out that you want to keep it, and once to actually process it). That way, you could also get rid of the filteredFiles list since you can just skip the files you are not interested in and process the other ones.
Related
I have two text files, Source.txt and Target.txt. The source will never be modified and contain N lines of text. So, I want to delete a specific line of text in Target.txt, and replace by an specific line of text from Source.txt, I know what number of line I need, actually is the line number 2, both files.
I haven something like this:
string line = string.Empty;
int line_number = 1;
int line_to_edit = 2;
using StreamReader reader = new StreamReader(#"C:\target.xml");
using StreamWriter writer = new StreamWriter(#"C:\target.xml");
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
writer.WriteLine(line);
line_number++;
}
But when I open the Writer, the target file get erased, it writes the lines, but, when opened, the target file only contains the copied lines, the rest get lost.
What can I do?
the easiest way is :
static void lineChanger(string newText, string fileName, int line_to_edit)
{
string[] arrLine = File.ReadAllLines(fileName);
arrLine[line_to_edit - 1] = newText;
File.WriteAllLines(fileName, arrLine);
}
usage :
lineChanger("new content for this line" , "sample.text" , 34);
You can't rewrite a line without rewriting the entire file (unless the lines happen to be the same length). If your files are small then reading the entire target file into memory and then writing it out again might make sense. You can do that like this:
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
int line_to_edit = 2; // Warning: 1-based indexing!
string sourceFile = "source.txt";
string destinationFile = "target.txt";
// Read the appropriate line from the file.
string lineToWrite = null;
using (StreamReader reader = new StreamReader(sourceFile))
{
for (int i = 1; i <= line_to_edit; ++i)
lineToWrite = reader.ReadLine();
}
if (lineToWrite == null)
throw new InvalidDataException("Line does not exist in " + sourceFile);
// Read the old file.
string[] lines = File.ReadAllLines(destinationFile);
// Write the new file over the old file.
using (StreamWriter writer = new StreamWriter(destinationFile))
{
for (int currentLine = 1; currentLine <= lines.Length; ++currentLine)
{
if (currentLine == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
writer.WriteLine(lines[currentLine - 1]);
}
}
}
}
}
If your files are large it would be better to create a new file so that you can read streaming from one file while you write to the other. This means that you don't need to have the whole file in memory at once. You can do that like this:
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
int line_to_edit = 2;
string sourceFile = "source.txt";
string destinationFile = "target.txt";
string tempFile = "target2.txt";
// Read the appropriate line from the file.
string lineToWrite = null;
using (StreamReader reader = new StreamReader(sourceFile))
{
for (int i = 1; i <= line_to_edit; ++i)
lineToWrite = reader.ReadLine();
}
if (lineToWrite == null)
throw new InvalidDataException("Line does not exist in " + sourceFile);
// Read from the target file and write to a new file.
int line_number = 1;
string line = null;
using (StreamReader reader = new StreamReader(destinationFile))
using (StreamWriter writer = new StreamWriter(tempFile))
{
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
writer.WriteLine(line);
}
line_number++;
}
}
// TODO: Delete the old file and replace it with the new file here.
}
}
You can afterwards move the file once you are sure that the write operation has succeeded (no excecption was thrown and the writer is closed).
Note that in both cases it is a bit confusing that you are using 1-based indexing for your line numbers. It might make more sense in your code to use 0-based indexing. You can have 1-based index in your user interface to your program if you wish, but convert it to a 0-indexed before sending it further.
Also, a disadvantage of directly overwriting the old file with the new file is that if it fails halfway through then you might permanently lose whatever data wasn't written. By writing to a third file first you only delete the original data after you are sure that you have another (corrected) copy of it, so you can recover the data if the computer crashes halfway through.
A final remark: I noticed that your files had an xml extension. You might want to consider if it makes more sense for you to use an XML parser to modify the contents of the files instead of replacing specific lines.
When you create a StreamWriter it always create a file from scratch, you will have to create a third file and copy from target and replace what you need, and then replace the old one.
But as I can see what you need is XML manipulation, you might want to use XmlDocument and modify your file using Xpath.
You need to Open the output file for write access rather than using a new StreamReader, which always overwrites the output file.
StreamWriter stm = null;
fi = new FileInfo(#"C:\target.xml");
if (fi.Exists)
stm = fi.OpenWrite();
Of course, you will still have to seek to the correct line in the output file, which will be hard since you can't read from it, so unless you already KNOW the byte offset to seek to, you probably really want read/write access.
FileStream stm = fi.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
with this stream, you can read until you get to the point where you want to make changes, then write. Keep in mind that you are writing bytes, not lines, so to overwrite a line you will need to write the same number of characters as the line you want to change.
I guess the below should work (instead of the writer part from your example). I'm unfortunately with no build environment so It's from memory but I hope it helps
using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite)))
{
var destinationReader = StreamReader(fs);
var writer = StreamWriter(fs);
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
destinationReader .ReadLine();
}
line_number++;
}
}
The solution works fine. But I need to change single-line text when the same text is in multiple places. For this, need to define a trackText to start finding after that text and finally change oldText with newText.
private int FindLineNumber(string fileName, string trackText, string oldText, string newText)
{
int lineNumber = 0;
string[] textLine = System.IO.File.ReadAllLines(fileName);
for (int i = 0; i< textLine.Length;i++)
{
if (textLine[i].Contains(trackText)) //start finding matching text after.
traced = true;
if (traced)
if (textLine[i].Contains(oldText)) // Match text
{
textLine[i] = newText; // replace text with new one.
traced = false;
System.IO.File.WriteAllLines(fileName, textLine);
lineNumber = i;
break; //go out from loop
}
}
return lineNumber
}
I have two text files, Source.txt and Target.txt. The source will never be modified and contain N lines of text. So, I want to delete a specific line of text in Target.txt, and replace by an specific line of text from Source.txt, I know what number of line I need, actually is the line number 2, both files.
I haven something like this:
string line = string.Empty;
int line_number = 1;
int line_to_edit = 2;
using StreamReader reader = new StreamReader(#"C:\target.xml");
using StreamWriter writer = new StreamWriter(#"C:\target.xml");
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
writer.WriteLine(line);
line_number++;
}
But when I open the Writer, the target file get erased, it writes the lines, but, when opened, the target file only contains the copied lines, the rest get lost.
What can I do?
the easiest way is :
static void lineChanger(string newText, string fileName, int line_to_edit)
{
string[] arrLine = File.ReadAllLines(fileName);
arrLine[line_to_edit - 1] = newText;
File.WriteAllLines(fileName, arrLine);
}
usage :
lineChanger("new content for this line" , "sample.text" , 34);
You can't rewrite a line without rewriting the entire file (unless the lines happen to be the same length). If your files are small then reading the entire target file into memory and then writing it out again might make sense. You can do that like this:
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
int line_to_edit = 2; // Warning: 1-based indexing!
string sourceFile = "source.txt";
string destinationFile = "target.txt";
// Read the appropriate line from the file.
string lineToWrite = null;
using (StreamReader reader = new StreamReader(sourceFile))
{
for (int i = 1; i <= line_to_edit; ++i)
lineToWrite = reader.ReadLine();
}
if (lineToWrite == null)
throw new InvalidDataException("Line does not exist in " + sourceFile);
// Read the old file.
string[] lines = File.ReadAllLines(destinationFile);
// Write the new file over the old file.
using (StreamWriter writer = new StreamWriter(destinationFile))
{
for (int currentLine = 1; currentLine <= lines.Length; ++currentLine)
{
if (currentLine == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
writer.WriteLine(lines[currentLine - 1]);
}
}
}
}
}
If your files are large it would be better to create a new file so that you can read streaming from one file while you write to the other. This means that you don't need to have the whole file in memory at once. You can do that like this:
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
int line_to_edit = 2;
string sourceFile = "source.txt";
string destinationFile = "target.txt";
string tempFile = "target2.txt";
// Read the appropriate line from the file.
string lineToWrite = null;
using (StreamReader reader = new StreamReader(sourceFile))
{
for (int i = 1; i <= line_to_edit; ++i)
lineToWrite = reader.ReadLine();
}
if (lineToWrite == null)
throw new InvalidDataException("Line does not exist in " + sourceFile);
// Read from the target file and write to a new file.
int line_number = 1;
string line = null;
using (StreamReader reader = new StreamReader(destinationFile))
using (StreamWriter writer = new StreamWriter(tempFile))
{
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
writer.WriteLine(line);
}
line_number++;
}
}
// TODO: Delete the old file and replace it with the new file here.
}
}
You can afterwards move the file once you are sure that the write operation has succeeded (no excecption was thrown and the writer is closed).
Note that in both cases it is a bit confusing that you are using 1-based indexing for your line numbers. It might make more sense in your code to use 0-based indexing. You can have 1-based index in your user interface to your program if you wish, but convert it to a 0-indexed before sending it further.
Also, a disadvantage of directly overwriting the old file with the new file is that if it fails halfway through then you might permanently lose whatever data wasn't written. By writing to a third file first you only delete the original data after you are sure that you have another (corrected) copy of it, so you can recover the data if the computer crashes halfway through.
A final remark: I noticed that your files had an xml extension. You might want to consider if it makes more sense for you to use an XML parser to modify the contents of the files instead of replacing specific lines.
When you create a StreamWriter it always create a file from scratch, you will have to create a third file and copy from target and replace what you need, and then replace the old one.
But as I can see what you need is XML manipulation, you might want to use XmlDocument and modify your file using Xpath.
You need to Open the output file for write access rather than using a new StreamReader, which always overwrites the output file.
StreamWriter stm = null;
fi = new FileInfo(#"C:\target.xml");
if (fi.Exists)
stm = fi.OpenWrite();
Of course, you will still have to seek to the correct line in the output file, which will be hard since you can't read from it, so unless you already KNOW the byte offset to seek to, you probably really want read/write access.
FileStream stm = fi.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
with this stream, you can read until you get to the point where you want to make changes, then write. Keep in mind that you are writing bytes, not lines, so to overwrite a line you will need to write the same number of characters as the line you want to change.
I guess the below should work (instead of the writer part from your example). I'm unfortunately with no build environment so It's from memory but I hope it helps
using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite)))
{
var destinationReader = StreamReader(fs);
var writer = StreamWriter(fs);
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
destinationReader .ReadLine();
}
line_number++;
}
}
The solution works fine. But I need to change single-line text when the same text is in multiple places. For this, need to define a trackText to start finding after that text and finally change oldText with newText.
private int FindLineNumber(string fileName, string trackText, string oldText, string newText)
{
int lineNumber = 0;
string[] textLine = System.IO.File.ReadAllLines(fileName);
for (int i = 0; i< textLine.Length;i++)
{
if (textLine[i].Contains(trackText)) //start finding matching text after.
traced = true;
if (traced)
if (textLine[i].Contains(oldText)) // Match text
{
textLine[i] = newText; // replace text with new one.
traced = false;
System.IO.File.WriteAllLines(fileName, textLine);
lineNumber = i;
break; //go out from loop
}
}
return lineNumber
}
I have to create two folders inside of a zip file that I create programmatically using ICSharpCode.SharZipLib.Zip. I want to:
private void AddToZipStream(byte[] inputStream, ZipOutputStream zipStream, string fileName, string fileExtension)
{
var courseName = RemoveSpecialCharacters(fileName);
var m_Bytes = inputStream;
if ((m_Bytes != null) && (zipStream != null))
{
var newEntry = new ZipEntry(ZipEntry.CleanName(string.Concat(courseName, fileExtension)));
newEntry.DateTime = DateTime.Now;
newEntry.Size = m_Bytes.Length;
zipStream.PutNextEntry(newEntry);
zipStream.Write(m_Bytes, 0, m_Bytes.Length);
zipStream.CloseEntry();
zipStream.UseZip64 = UseZip64.Off;
}
}
How do I create a directory using ZipEntry and how do then add files to the directory located inside of the Zip archive?
I figured it out:
You can simply do new ZipEntry("Folder1/Archive.txt"); and new ZipEntry("Folder2/Archive2.txt");
The answer above will work for several scenarios, but it will not work when you want to add an empty folder to a zip file.
I sifted through the SharpZipLib code and found that the only thing you need to do to create a folder is a trailing "/" forward slash on the ZipEntry name.
Here's the code from the library:
public bool IsDirectory {
get {
int nameLength = name.Length;
bool result =
((nameLength > 0) &&
((name[nameLength - 1] == '/') || (name[nameLength - 1] == '\\'))) ||
HasDosAttributes(16)
;
return result;
}
}
So, just create folders as though they are files with ZipEntry, and put a forward slash on the end. It works. I've tested it.
The best solution at our project was to switch to the way better
https://dotnetzip.codeplex.com
https://github.com/haf/DotNetZip.Semverd
the methods are more straight forward to use
I have an application where I need to get the objects that are embedded in excel should be stored in some location through code.
connection.Close();
//connection.ResetState();
string embeddingPartString;
//ArrayList chkdLstEmbeddedFiles = new ArrayList(); ;
List<String> chkdLstEmbeddedFiles = new List<string>();
//string fileName = txtSourceFile.Text;
if (filepath == string.Empty || !System.IO.File.Exists(filepath))
{
return;
}
// Open the package file
pkg = Package.Open(filepath, FileMode.Open, FileAccess.ReadWrite);
System.IO.FileInfo fi = new System.IO.FileInfo(filepath);
string extension = fi.Extension.ToLower();
if ((extension == ".docx") || (extension == ".dotx") || (extension == ".docm") || (extension == ".dotm"))
{
embeddingPartString = "/word/embeddings/";
}
else if ((extension == ".xlsx") || (extension == ".xlsm") || (extension == ".xltx") || (extension == ".xltm"))
{
embeddingPartString = "/xl/embeddings/";
}
else
{
embeddingPartString = "/ppt/embeddings/";
}
// Get the embedded files names.
foreach (PackagePart pkgPart in pkg.GetParts())
{
if (pkgPart.Uri.ToString().StartsWith(embeddingPartString))
{
string fileName1 = pkgPart.Uri.ToString().Remove(0, embeddingPartString.Length);
chkdLstEmbeddedFiles.Add(fileName1);
}
}
//pkg.Close();
if (chkdLstEmbeddedFiles.Count == 0)
//MessageBox.Show("The file does not contain any embedded files.");
// Open the package and loop through parts
// Check if the part uri to find if it contains the selected items in checked list box
pkg = Package.Open(filepath);
foreach (PackagePart pkgPart in pkg.GetParts())
{
for (int i = 0; i < chkdLstEmbeddedFiles.Count; i++)
{
object chkditem = chkdLstEmbeddedFiles[i];
if (pkgPart.Uri.ToString().Contains(embeddingPartString + chkdLstEmbeddedFiles[i].ToString()))
{
// Get the file name
string fileName1 = pkgPart.Uri.ToString().Remove(0, embeddingPartString.Length);
// Get the stream from the part
System.IO.Stream partStream = pkgPart.GetStream();
//string filePath = txtDestinationFolder.Text + "\\" + fileName1;
// Write the steam to the file.
Environment.GetEnvironmentVariable("userprofile");
System.IO.FileStream writeStream = new System.IO.FileStream(#"C:\Users\jyshet\Documents\MicrosoftDoc_File.docx", FileMode.Create, FileAccess.Write);
ReadWriteStream(pkgPart.GetStream(), writeStream);
// If the file is a structured storage file stored as a oleObjectXX.bin file
// Use Ole10Native class to extract the contents inside it.
if (fileName1.Contains("oleObject"))
{
// The Ole10Native class is defined in Ole10Native.cs file
// Ole10Native.ExtractFile(filePath, txtDestinationFolder.Text);
}
}
}
}
pkg.Close();
I am able to download only doc file and rest are not seen like txt and mht files. Please suggest what to do in this case. My problem is any objects embedded in excel should be downloaded thru code and saved in destination.
I'm using a C# ASP.NET application i have folder name xyz and in this folder some file are stored like jpg, doc etc with their specific name.
But when i add a file which already exists in this folder, but was saved under different name.
I want to ask how it is possible to find such file that have different name but actually is the same?
Your question is very difficult to understand, but I think you're asking how to identify duplicate files: different files that have the same contents.
One way to do that is to hash the contents of each file (using a hash function such as SHA-1) and store the results in a Dictionary, using the hash as the key, and a List of filenames as the value. If two (or more) files have the same contents, they'll have the same hash value, so they'll all be filed under the same key in the dictionary. After you've hashed all the files and put the results into the dictionary, you can go through its values and check whether any of the lists contain more than one item.
void SaveFile(string fileName)
{
string folderPath = Server.MapPath("~/xyz");
DirectoryInfo dirInfo = new DirectoryInfo(folderPath);
FileInfo fileInfo = new FileInfo(fileName);
// comparison algorithm based on size and creation date
bool exists = (from fi in dirInfo.EnumerateFiles(folderPath)
where fi.Size == fileInfo.Size &&
fi.CreationTimeUtc == fileInfo.CreationTimeUtc
select fi).Any();
// comparison algorithm based on hash
string fileHash = ComputeHash(fileInfo.FullPath);
bool exists = (from fi in dirInfo.EnumerateFiles(folderPath)
where String.Equals(
ComputeHash(fi.FullPath),
fileHash,
StringComparison.Ordinal)
select fi).Any();
}
A sample how to get the md5 hash of a file, see more.
string ComputeHash(string fileName)
{
byte[] bytes;
using (Stream stream = new FileStream(fileName, FileMode.Open))
{
MD5 md5 = new MD5CryptoServiceProvider();
bytes = md5.ComputeHash(stream);
}
StringBuilder sb = new StringBuilder(retVal.Length);
for (int i = 0; i < bytes.Length; i++)
{
sb.Append(bytes[i].ToString("x2"));
}
return sb.ToString();
}
If i understand the question correctly, you want to know that 2 files are the same file, even though they have a different filename.
I suppose you can read each file byte by byte and compare:
public static bool AreEqual(string f1, string f2)
{
var fi1 = new FileInfo(f1);
var fi2 = new FileInfo(f2);
// first check that they are the same size, obviously a pre-req for them being equal
if (f1.Length != f2.Length)
{
return false;
}
var sr1 = new FileStream(f1, FileMode.Open);
var sr2 = new FileStream(f2, FileMode.Open);
for (int i = 0; i < f1.Length; i++)
{
byte[] left = new byte[1];
byte[] right = new byte[1];
sr1.Read(left, i, 1);
sr2.Read(right, i, 1);
if (left[0] != right[0])
{
return false;
}
}
return true;
}