Load XML document in read-only access mode - c#

How do I load an XML document in read-only mode?
I have an XML file which is opened in another process and I want to load it in my C# application as read-only.
XmlDocument.Load("file.xml") obviously throws this error:
Process cannot access a file because it is being used by another
process
So I tried stream reader too:
FileStream fs = new FileStream("file.xml", FileMode.Open, FileAccess.Read);
xmldoc.Load(fs);
But it also throws the same error.
So how can I access my XML Document in read-only mode?
Update
I tried XPathDocument and FileStream("file.xml", FileMode.Open, FileAccess.Read,FileShare.Read) as well. But neither of them solved the problem.

This class Shows read xml file in read only mode.
public List<string[]> GetRunningOrderOnTable(string tableNo, int shopid)
{
try
{
XmlDocument xmlDoc = new XmlDocument();
string xmlFilePath = #"C:\inetpub\wwwroot\ShopAPI\XmlData\RunningTables.xml";
//string xmlFilePath = HttpContext.Current.Server.MapPath("~/XmlData/RunningTables.xml");
// Option 1
// FileStream xmlFile = new FileStream(xmlFilePath, FileMode.Open,
//FileAccess.Read, FileShare.Read);
// xmlDoc.Load(xmlFile);
// Option 2
using (Stream s = File.OpenRead(xmlFilePath))
{
xmlDoc.Load(s);
}
//xmlDoc.Load(xmlFilePath);
List<string[]> st = new List<string[]>();
XmlNodeList userNodes = xmlDoc.SelectNodes("//Tables/Table");
if (userNodes != null)
{
foreach (XmlNode userNode in userNodes)
{
string tblNo = userNode.Attributes["No"].Value;
string sid = userNode.Attributes["ShopID"].Value;
if (tblNo == tableNo && sid == shopid.ToString())
{
string[] str = new string[5];
str[0] = userNode.Attributes["No"].Value;
str[1] = userNode.InnerText; // OrderNumber
str[2] = userNode.Attributes["OrderID"].Value;
str[3] = userNode.Attributes["OrderedOn"].Value;
str[4] = userNode.Attributes["TotalAmount"].Value;
st.Add(str);
}
}
}
else return new List<string[]>();
return st;
}
catch (Exception ex)
{
CustomLogging.Log("RunningTables.xml GetRunningOrderOnTable Error " + ex.StackTrace, LoggingType.XMLRead);
return new List<string[]>();
}
}

Given you've said that FileShare.Read doesn't work, it would appear that the other process has the file open for writing.
You could try opening it with FileAccess.Read and FileShare.ReadWrite, in which case you'll need to handle any errors that may occur if the other process does actually write to the file.
If that doesn't work, it's likely that the other process has it opened with FileShare.None, in which case there's nothing you can do about it. To check this, try opening the file with, say, Notepad.
But is it still possible for FileShare.ReadWrite to throws error if it works in most cases?
You will only get an error if another process has already opened the file using FileShare.None. You've confirmed that this isn't the case when it's open in Microsoft Word, so you should be OK.

Related

c# how to end streamreader

I am doing a project Windows form for assignment in Uni, I want to search an already created text file to match a first name and last name then write some additional information if the name and last name exist. I have the code constructed and showing no errors, however when I run and attempt to add information I am being provided with an error which essentially says the next process (Streamreader writer can not access the file as it is already in use by another process) I assume this process is streamreader, I have tried to code it to stop reading to no avail. I am in my first 3 months learning coding and would appreciate some assistance if possible, I have put a snippet of my code below.
//check if there is a file with that name
if (File.Exists(sFile))
{
using (StreamReader sr = new StreamReader(sFile))
{
//while there is more data to read
while (sr.Peek() != -1)
{
//read first name and last name
sFirstName = sr.ReadLine();
sLastName = sr.ReadLine();
}
{
//does this name match?
if (sFirstName + sLastName == txtSearchName.Text)
sr.Close();
}
//Process write to file
using (StreamWriter sw = new StreamWriter(sFile, true))
{
sw.WriteLine("First Name:" + sFirstName);
sw.WriteLine("Last Name:" + sLastName);
sw.WriteLine("Gender:" + sGender);
}
You are using your writer inside the reader, using the same file.
A using disposes the object inside it, after the closing curly braces.
using(StreamReader reader = new StreamReader("foo")){
//... some stuff
using(Streamwriter writer = new StreamWriter("foo")){
}
}
Do it like so :
using(StreamReader reader = new StreamReader("foo")){
//... some stuff
}
using(Streamwriter writer = new StreamWriter("foo")){
}
As per my comment regarding the using statement.
Rearrange to the below. I've tested locally and it seems to work.
using (StreamReader sr = new StreamReader(sfile))
{
//while there is more data to read
while (sr.Peek() != -1)
{
//read first name and last name
sFirstName = sr.ReadLine();
sLastName = sr.ReadLine();
//does this name match?
if (sFirstName + sLastName == txtSearchName.Text)
break;
}
}
using (StreamWriter sw = new StreamWriter(sfile, true))
{
sw.WriteLine("First Name:" + sFirstName);
sw.WriteLine("Last Name:" + sLastName);
sw.WriteLine("Gender:" + sGender);
}
I've replaced the sr.Close with a break statement to exit out. Closing the reader causes the subsequent peek to error as it's closed.
Also, I've noticed that you are not setting gender? unless its set elsewhere.
hope that helps
You can use FileStream. It gives you many options to work with file:
var fileStream = new FileStream("FileName", FileMode.Open,
FileAccess.Write, FileShare.ReadWrite);
var fileStream = new FileStream("fileName", FileMode.Open,
FileAccess.ReadWrite, FileShare.ReadWrite);
I think this is what you want/need. You can't append to a file the way you are trying to do it. Instead you'll want to read your input file, and write a temp file as you are reading through. And, whenever your line matches your requirements, then you can write the line with your modifications.
string inputFile = "C:\\temp\\StreamWriterSample.txt";
string tempFile = "C:\\temp\\StreamWriterSampleTemp.txt";
using (StreamWriter sw = new StreamWriter(tempFile))//get a writer ready
{
using (StreamReader sr = new StreamReader(inputFile))//get a reader ready
{
string currentLine = string.Empty;
while ((currentLine = sr.ReadLine()) != null)
{
if (currentLine.Contains("Clients"))
{
sw.WriteLine(currentLine + " modified");
}
else
{
sw.WriteLine(currentLine);
}
}
}
}
//now lets crush the old file with the new file
File.Copy(tempFile, inputFile, true);

Why does FileStream sometimes ignore invisible characters?

I have two blocks of code that I've tried using for reading data out of a file-stream in C#. My overall goal here is to try and read each line of text into a list of strings, but they are all being read into a single string (when opened with read+write access together)...
I am noticing that the first block of code correctly reads in all of my carriage returns and line-feeds, and the other ignores them. I am not sure what is really going on here. I open up the streams in two different ways, but that shouldn't really matter right? Well, in any case here is the first block of code (that correctly reads-in my white-space characters):
StreamReader sr = null;
StreamWriter sw = null;
FileStream fs = null;
List<string> content = new List<string>();
List<string> actual = new List<string>();
string line = string.Empty;
// first, open up the file for reading
fs = File.OpenRead(path);
sr = new StreamReader(fs);
// read-in the entire file line-by-line
while(!string.IsNullOrEmpty((line = sr.ReadLine())))
{
content.Add(line);
}
sr.Close();
Now, here is the block of code that ignores all of the white-space characters (i.e. line-feed, carriage-return) and reads my entire file in one line.
StreamReader sr = null;
StreamWriter sw = null;
FileStream fs = null;
List<string> content = new List<string>();
List<string> actual = new List<string>();
string line = string.Empty;
// first, open up the file for reading/writing
fs = File.Open(path, FileMode.Open);
sr = new StreamReader(fs);
// read-in the entire file line-by-line
while(!string.IsNullOrEmpty((line = sr.ReadLine())))
{
content.Add(line);
}
sr.Close();
Why does Open cause all data to be read as a single line, and OpenRead works correctly (reads data as multiple lines)?
UPDATE 1
I have been asked to provide the text of the file that reproduces the problem. So here it is below (make sure that CR+LF is at the end of each line!! I am not sure if that will get pasted here!)
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;$$$$$$$$$ $$$$$$$
;$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
;
;
;
UPDATE 2
An exact block of code that reproduces the problem (using the text above for the file). In this case I am actually seeing the problem WITHOUT trying Open and only using OpenRead.
StreamReader sr = null;
StreamWriter sw = null;
FileStream fs = null;
List<string> content = new List<string>();
List<string> actual = new List<string>();
string line = string.Empty;
try
{
// first, open up the file for reading/writing
fs = File.OpenRead(path);
sr = new StreamReader(fs);
// read-in the entire file line-by-line
while(!string.IsNullOrEmpty((line = sr.ReadLine())))
{
content.Add(line);
}
sr.Close();
// now, erase the contents of the file
File.WriteAllText(path, string.Empty);
// make sure that the contents of the file have been erased
fs = File.OpenRead(path);
sr = new StreamReader(fs);
if (!string.IsNullOrEmpty(line = sr.ReadLine()))
{
Trace.WriteLine("Failed: Could not erase the contents of the file.");
Assert.Fail();
}
else
{
Trace.WriteLine("Passed: Successfully erased the contents of the file.");
}
// now, attempt to over-write the contents of the file
fs.Close();
fs = File.OpenWrite(path);
sw = new StreamWriter(fs);
foreach(var l in content)
{
sw.Write(l);
}
// read back the over-written contents of the file
fs.Close();
fs = File.OpenRead(path);
sr = new StreamReader(fs);
while (!string.IsNullOrEmpty((line = sr.ReadLine())))
{
actual.Add(line);
}
// make sure the contents of the file are correct
if(content.SequenceEqual(actual))
{
Trace.WriteLine("Passed: The contents that were over-written are correct!");
}
else
{
Trace.WriteLine("Failed: The contents that were over-written are not correct!");
}
}
finally
{
// close out all the streams
fs.Close();
// finish-up with a message
Trace.WriteLine("Finished running the overwrite-file test.");
}
Your new file generated by
foreach(var l in content)
{
sw.Write(l);
}
does not contain end-of-line characters because end-of-line characters are not included in content.
As #DaveKidder points out in this thread over here, the spec for StreamReader.ReadLine specifically says that the resulting line does not include end of line.
When you do
while(!string.IsNullOrEmpty((line = sr.ReadLine())))
{
content.Add(line);
}
sr.Close();
You are losing end of line characters.

Saving multiple values in isolated storage

I am working on a Windows Phone 8 application. In my application, I have to store the scores of each attempt.
Eg:
1st attempt: orange 10 ,apple 20 ,mango 30.
2nd attempt: orange 5 ,apple 20 ,mango 25.
3rd attempt: orange 15 ,apple 25 ,mango 5.
and so on...
I have a score sheet where I need to show the values of each attempt.
This is how I am trying to save the values:
To write values
try
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (StreamWriter sw = new StreamWriter(store.OpenFile(myObject.Title + "_State.xml", FileMode.Create, FileAccess.Write)))
{
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
serializer.Serialize(sw, myObject);
serializer = null;
}
}
}
catch (Exception)
{
throw;
}
To read
MyObject myObject = null;
try
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
// Read application settings.
if (isoStore.FileExists((myObject.Title + "_State.xml"))
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
using (StreamReader SR = new StreamReader(store.OpenFile((myObject.Title + "_State.xml", FileMode.Open, FileAccess.Read)))
{
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
myObject = (MyObject)serializer.Deserialize(SR);
serializer = null;
}
}
}
else
{
// If setting does not exists return default setting.
myObject = new MyObject();
}
}
}
catch (Exception)
{
throw;
}
return myObject;
But in the above code, every time the values are replaced instead of added to previous values.
How do I add the attempts and their values to the same file?
Try to change FileMode when writing to the file to FileMode.Append instead of FileMode.Create :
store.OpenFile(myObject.Title + "_State.xml", FileMode.Append, FileAccess.Write)
About FileMode.Append from MSDN link above :
"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. Trying to seek to a position before the end of the file throws an IOException exception, and any attempt to read fails and throws a NotSupportedException exception."

c# Replace openfileDialog to a none gui stream

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

Split large XML file after string found

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.

Categories

Resources