Have a slight issue that I need a second pair of eyes because I am at the point where the code is just being hacked apart instead of taking time needed to break it down.
The end goal of the program is to read in X number of files depending on users choices, take those files put them into memory all together, and then append them all to one Word file.
It is only writing out the first file. If I change to a WriteFile method it only writes out the last file. I would like it to write out all files
Any tips in the right direction would be appreciated.
private void cmdSubmit_Click(object sender, EventArgs e)
{
SaveFileDialog saveFile = new SaveFileDialog();
saveFile.Filter = "Word Files (*.doc)|*.doc";
saveFile.FilterIndex = 1;
saveFile.RestoreDirectory = true;
string dirPath = "";
FileStream destStream = (dynamic)null;
MemoryStream outStream = new MemoryStream();
if (saveFile.ShowDialog() == DialogResult.OK)
{
dirPath = saveFile.FileName;
destStream = new FileStream(dirPath, FileMode.Create, FileAccess.Write);
destStream.Close();
}
DirectoryInfo di = new DirectoryInfo(#"FilePath");
FileInfo[] args = di.GetFiles("*.doc");
foreach (object itemChecked in chkLB.CheckedItems)
{
//loop through checked items and add to file
string fileStream = di.ToString() + itemChecked.ToString() + ".doc";//READ IN FILES
FileStream inFile = File.OpenRead(fileStream); //Read the files one by one
outStream.SetLength(inFile.Length);
inFile.Read(outStream.GetBuffer(), 0, (int)inFile.Length);
outStream.Flush();
inFile.Close();
fileSave(outStream, dirPath);
//MessageBox.Show("Item with title: \"" + itemChecked.ToString()); Nice trick to add "" around a value
}
//Open File
//Microsoft.Office.Interop.Word.Application word = new Microsoft.Office.Interop.Word.Application();
//Document openDoc = word.Documents.Open(dirPath);
MessageBox.Show("DONE");
}
public static void fileSave(MemoryStream memStream, string saveFile)
{
//FileStream fileOut = File.Open(saveFile, FileMode.Append);
if (!File.Exists(saveFile))
{
FileStream fileOut = new FileStream(saveFile, FileMode.OpenOrCreate, FileAccess.Write);
//stream writter?
memStream.WriteTo(fileOut);
fileOut.Flush();
fileOut.Close();
}
else
{
FileStream fileOut = new FileStream(saveFile, FileMode.Append, FileAccess.Write);
memStream.WriteTo(fileOut);
fileOut.Flush();
fileOut.Close();
}
}
Use open XMl to merge two word files into one.
AltChunk is the answer for your question.
http://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.altchunk.aspx
There is two answer for this question.
Answer 1.
If all you're doing is reading files and marge them together to a new file, you might not need to write code. You can use command:
C:\> copy x.doc+y.doc+z.doc output.doc
Now call this via Process.Start when you want.
Answer 2:
string[] files = { #"E:\x.txt", #"E:\y.txt", #"E:\z.txt" };
FileStream outputFile = new FileStream(#"E:\output.txt", FileMode.Create);
using (BinaryWriter ws = new BinaryWriter(outputFile))
{
foreach (string file in files)
{
ws.Write(System.IO.File.ReadAllBytes(file));
}
}
Well...the simple way wouldn't be much more difficult than this:
public static void Concatenate( IEnumerable<FileInfo> files , FileInfo destination , bool overWriteDestination = true )
{
FileMode mode = overWriteDestination ? FileMode.Create : FileMode.Append ;
using ( FileStream tgt = destination.Open( mode , FileAccess.Write , FileShare.Read ) )
{
foreach( FileInfo file in files )
{
using ( FileStream src = file.Open( FileMode.Open , FileAccess.Read , FileShare.Read ) )
{
const int bufferSize = 64*1024; // 64k buffer OK?
src.CopyTo( tgt , bufferSize ) ;
}
}
tgt.Flush() ;
}
return;
}
...
DirectoryInfo dir = new DirectoryInfo( #"C:\foo") ;
FileInfo target = new FileInfo( #"C:\foo.glob") ;
Concatenate( dir.EnumerateFiles( "*.*" ) , target , true ) ;
Which will do what you ask (concatenate the files). But at the end of the day, all you've got is all the various octets concatenated together. If you're expecting a single MS Word document to result that is the functional concatenation of the pages in each file, it's not going to happen.
Easily.
That's a rather more complicated process. These might be of use:
Append multiple DOCX files together
http://blogs.msdn.com/b/brian_jones/archive/2008/12/08/the-easy-way-to-assemble-multiple-word-documents.aspx
All of these techniques make the assumption that you're working with *.docx files (which are XML) rather than the binary *.doc files. If you've got to deal with *.doc files, Good luck. You're probably better off converting them to RTF and parsing that, or converting them to *.docx.
Related
My class which holds the constructor method of writing to a file
class Writer
{
public Writer(string filename, List<string> data)
{
FileStream ostrm;
StreamWriter writer;
TextWriter oldOut = Console.Out;
try
{
ostrm = new FileStream("C:/Users/kyle/Desktop/ConferenceSoftware/" + filename + ".txt", FileMode.OpenOrCreate, FileAccess.Write);
writer = new StreamWriter( ostrm );
}
catch (Exception e)
{
Console.WriteLine("Cannot open " + filename + ".txt for writing");
Console.WriteLine(e.Message);
return;
}
Console.SetOut(writer);
foreach (var _data in data)
{
Console.WriteLine( _data );
}
Console.SetOut(oldOut);
writer.Close();
ostrm.Close();
}
}
Inside my main method:
List<string> dataToAdd = new List<string>();
dataToAdd.Add("Example");
new Writer(Settings.ConferenceRoomName, dataToAdd);
However, if I already have text inside the file, instead appending to the file from the last line, it does it from the first line, ie:
Example // new added through method
Line that already exists // already in file
Line that already exists // already in file
// this is where I wanted it but it goes to the top
Any help would be appreciated, I don't see where I could target what line to start writing at.
Thanks in advance.
You need to use FileMode.Append instead of OpenOrCreate:
ostrm = new FileStream("C:/Users/kyle/Desktop/ConferenceSoftware/" +
filename + ".txt", FileMode.Append, FileAccess.Write);
See documentation (about FileMode.Append):
Opens the file if it exists and seeks to the end of the file, or creates a new file. This requires FileIOPermissionAccess.Append permission. FileMode.Append can be used only in conjunction with FileAccess.Write...
So it will also create the file if it does not exist.
If you don't want to create a non-existing file, you can check if it exists using File.Exists() before opening the stream.
When you create a new FileStream with FileMode.OpenOrCreate, it writes over the existing file. If you would like to append to the file, use FileMode.Append instead:
FileMode.Append: Opens the file if it exists and seeks to the end of the file, or creates a new file. This requires FileIOPermissionAccess.Append permission.
ostrm = new FileStream("C:/Users/kyle/Desktop/ConferenceSoftware/" + filename + ".txt", FileMode.Append, FileAccess.Write);
You want file mode Append instead of OpenOrCreate:
ostrm = new FileStream("C:/Users/kyle/Desktop/ConferenceSoftware/" + filename + ".txt", FileMode.Append, FileAccess.Write);
Both will create a new file if it doesn't exist, the difference is that OpenOrCreate starts writing at the beginning of the file and Append starts writing at the end of the file.
Reference: FileMode Enumeration
I simply want to merge all text files in a given directory, similar to the following command prompt command:
cd $directory
copy * result.txt
I've written the following code, which almost accomplishes what I want, but it's doing something strange. When the StreamWriter writes the first file (or when i = 0), it doesn't actually write any content - the file size remains 0 bytes, despite the first file being ~300 KB. However, the other file writes execute successfully.
If I compare the output from the command prompt to the output from the C# code in diff, you can see that a large block of text is missing. Additionally, the command prompt result is 1,044 KB where the C# result is 700 KB.
string[] txtFiles = Directory.GetFiles(filepath);
using (StreamWriter writer = new StreamWriter(filepath + "result.txt"))
{
for (int i = 0; i < txtFiles.Length; i++)
{
using (StreamReader reader = File.OpenText(txtFiles[i]))
{
writer.Write(reader.ReadToEnd());
}
}
}
Am I using the StreamWriter / StreamReader incorrectly?
Minimalistic implementation, reading the bytes and writing them instead of using a stream for reading - please note, that you should handle the IOException correctly to avoid misbehavior:
var newline = Encoding.ASCII.GetBytes(Environment.NewLine);
var files = Directory.GetFiles(filepath);
try
{
using (var writer = File.Open(Path.Combine(filepath, "result.txt"), FileMode.Create))
foreach (var text in files.Select(File.ReadAllBytes))
{
writer.Write(text, 0, text.Length);
writer.Write(newline, 0, newline.Length);
}
}
catch (IOException)
{
// File might be used by different process or you have insufficient permissions
}
Here, hope it helps you. Note: By copying from a stream to another you save some ram and greatly improve performance.
class Program
{
static void Main(string[] args)
{
string filePath = #"C:\Users\FunkyName\Desktop";
string[] txtFiles = Directory.GetFiles(filePath, "*.txt");
using (Stream stream = File.Open(Path.Combine(filePath, "result.txt"), FileMode.OpenOrCreate))
{
for (int i = 0; i < txtFiles.Length; i++)
{
string fileName = txtFiles[i];
try
{
using (Stream fileStream = File.Open(fileName, FileMode.Open, FileAccess.Read))
{
fileStream.CopyTo(stream);
}
}
catch (IOException e)
{
// Handle file open exception
}
}
}
}
}
I wrote your code , it works properly! only change the line :
using (StreamWriter writer = new StreamWriter(filepath + "result.txt"))
to:
using (StreamWriter writer = new StreamWriter(filepath + "/result.txt"))
i guess you can't see the file because it is saved in another folder .
IsolatedStorageFile.FileExists(string path) works but StreamReader(string samePath) doesn't?
I have validated both paths are equal. I have no idea why the StreamReader explodes
List<ProjectObj> ret = new List<ProjectObj>();
IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication();
if (!file.DirectoryExists("/projects/")) //trying to validate the dir exists
return ret;
string[] fileNames = file.GetFileNames("/projects/");
foreach (string filename in fileNames)
{
if (!file.FileExists("/projects/" + filename)) //validate just one more time..
continue;
ProjectObj tempProj = new ProjectObj();
//Even with the validation it still breaks right here with the bellow error
StreamReader reader = new StreamReader("/projects/"+filename);
An exception of type 'System.IO.DirectoryNotFoundException' occurred
in mscorlib.ni.dll but was not handled in user code
Message:Could not find a part of the path
'C:\projects\Title_939931883.txt'.
Path is not same in both cases. In first case you are getting User store for application and then search for file in it. But in later case you are simply searching in base directory.
StreamReader constructor expects absolute path of file.
You need to create IsolatedStorageFileStream and pass it on to StreamReader -
using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream
("/projects/" + filename, FileMode.Open, file))
{
using (StreamReader reader = new StreamReader(fileStream ))
{
}
}
Give this one a try. Reading and writing files in IsolatedStorage has a different path and should be used like that. You should consider reading How to: Read and Write to Files in Isolated Storage.
public static List<ProjectObj> getProjectsList()
{
List<ProjectObj> ret = new List<ProjectObj>();
IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication();
if (!file.DirectoryExists("/projects/")) //trying to validate the dir exists
return ret;
string[] fileNames = file.GetFileNames("/projects/");
foreach (string filename in fileNames)
{
if (!file.FileExists("/projects/" + filename)) //validate just one more time...
continue;
ProjectObj tempProj = new ProjectObj();
using (var isoStream = new IsolatedStorageFileStream("/projects/" + filename, FileMode.Open, FileAccess.Read, FileShare.Read, file))
{
using (StreamReader reader = new StreamReader(isoStream))
{
}
}
This was the solution I came up with
List<ProjectObj> ret = new List<ProjectObj>();
IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication();
if (!file.DirectoryExists("/projects/"))
return ret;
foreach (String filename in file.GetFileNames("/projects/"))
{
IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream fileStream = myIsolatedStorage.OpenFile("/projects/"+filename, FileMode.Open, FileAccess.Read);
using (StreamReader reader = new StreamReader(fileStream))
{
String fileInfo = reader.ReadToEnd();
}
}
I don't know why I was getting the illegal operation on boot of app but I know why it was happening later on. I guess when you try and access the same file to quickly it causes errors. So I added in a fileshare and also I made sure to dispose of other accesses before this ran.
I am trying to separate the MIME gui from the code i need. I am almost there just one more gui element i dont know how to replace. This element is the openfiledialog. Here a code snippet.
Program.cs
var sfd = new OpenFileDialog();
sfd.FileName = "C:\\eml\\" + validOutputFilename;
try
{
var writer = new MimeMessageWriter();
using (var fs = sfd.OpenFile()) writer.Write(message, fs);
}
catch (Exception ex)
{
//ignore
// need to log
}
message is an IMessage. A class created to store the information about an eml file. The open file dialog is allowing you to put in the file name with an eml extension and that is all. write.Write expects an IMessage and a stream. Inside writer.Write the file is being written The only part of the file that uses this code is when the file itself is writen at the end and write out any attachments. Here are those code snippets.
*MimeMessageWriter
-the attachment uses it here
var embeddedMessage = attachment.OpenAsMessage();
var messageWriter = new MimeMessageWriter();
var msgStream = new MemoryStream();
messageWriter.Write(embeddedMessage, msgStream);
var messageAttachment = ew DotNetOpenMail.FileAttachment(msgStream.ToArray());
messageAttachment.ContentType = "message/rfc822";
messageAttachment.FileName = filename + ".eml";
outMessage.AddMixedAttachment(messageAttachment);
-write out the file part of the file
using (var sw = new StreamWriter(stream))
sw.Write(outMessage.ToDataString());
I want to replace openFileDialog with something that will allow me to pass the filename to write out file in the MimeMessageWriter
Replace
using (var fs = sfd.OpenFile()) writer.Write(message, fs);
with
string fileName = #"c:\eml\myAttachment.eml";
using ( FileStream fs = new FileStream( fileName, FileMode.CreateNew ) )
{
writer.Write( message, fs )
}
See also: http://msdn.microsoft.com/de-de/library/47ek66wy.aspx
What I have:
A large XML file # nearly 1 million lines worth of content. Example of content:
<etc35yh3 etc="numbers" etc234="a" etc345="date"><something><some more something></some more something></something></etc123>
<etc123 etc="numbers" etc234="a" etc345="date"><something><some more something></some more something></something></etc123>
<etc15y etc="numbers" etc234="a" etc345="date"><something><some more something></some more something></something></etc123>
^ repeat that by 900k or so lines (content changing of course)
What I need:
Search the XML file for "<etc123". Once found move (write) that line along with all lines below it to a separate XML file.
Would it be advisable to use a method such as File.ReadAllLines for the search portion? What would you all recommend for the writing portion. Line by line is not an option as far as I can tell as it would take much too long.
To quite literaly discard the content above your search string, I would not use File.ReadAllLines, as it would load the entire file into memory. Try File.Open and wrap it in a StreamReader. Loop on StreamReader.ReadLine, then start writing to a new StreamWriter, or do a byte copy on the underlying filestream.
An example of how to do so with StreamWriter/StreamReader alone is listed below.
//load the input file
//open with read and sharing
using (FileStream fsInput = new FileStream("input.txt",
FileMode.Open, FileAccess.Read, FileShare.Read))
{
//use streamreader to search for start
var srInput = new StreamReader(fsInput);
string searchString = "two";
string cSearch = null;
bool found = false;
while ((cSearch = srInput.ReadLine()) != null)
{
if (cSearch.StartsWith(searchString, StringComparison.CurrentCultureIgnoreCase)
{
found = true;
break;
}
}
if (!found)
throw new Exception("Searched string not found.");
//we have the data, write to a new file
using (StreamWriter sw = new StreamWriter(
new FileStream("out.txt", FileMode.OpenOrCreate, //create or overwrite
FileAccess.Write, FileShare.None))) // write only, no sharing
{
//write the line that we found in the search
sw.WriteLine(cSearch);
string cline = null;
while ((cline = srInput.ReadLine()) != null)
sw.WriteLine(cline);
}
}
//both files are closed and complete
You can copy with LINQ2XML
XElement doc=XElement.Load("yourXML.xml");
XDocument newDoc=new XDocument();
foreach(XElement elm in doc.DescendantsAndSelf("etc123"))
{
newDoc.Add(elm);
}
newDoc.Save("yourOutputXML.xml");
You could do one line at a time... Would not use read to end if checking contents of each line.
FileInfo file = new FileInfo("MyHugeXML.xml");
FileInfo outFile = new FileInfo("ResultFile.xml");
using(FileStream write = outFile.Create())
using(StreamReader sr = file.OpenRead())
{
bool foundit = false;
string line;
while((line = sr.ReadLine()) != null)
{
if(foundit)
{
write.WriteLine(line);
}
else if (line.Contains("<etc123"))
{
foundit = true;
}
}
}
Please note, this method may not produce valid XML, given your requirements.