I'm having an issue where I'm writing the contents of several xml files to one file. When I run the program, the output is in the proper format, but the words are out of order. An example of this:
My string is "<s:AttributeType name=\"Shift\" number=\"34\" nullable=\"true\" writeunknown=\"true\">"
So it should print <s:AttributeType name="Shift" number="34" nullable="true" writeunknown="true">
But instead <s:AttributeType name="Shift" writeunknown="true" number="34" nullable="true">
is returned.
Some of the file is written in using File.WriteAllText(#"C:\Users\status.xml", xsh);
Where 'xsh' is a variable containing a string.
The rest is written in using this loop:
foreach (var i in Numbers.GetWSnumber())
{
string contents = "";
string curFile = #"\\production\public\Staus\TStatus\WS" + i.SetId + ".xml";
if (File.Exists(curFile))
{
System.IO.StreamReader file = new System.IO.StreamReader(curFile);
while ((contents = file.ReadLine()) != null)
{
using (StreamWriter sw = File.AppendText(#"C:\Users\status.xml"))
{
sw.WriteLine(contents);
}
}
file.Close();
}
}
Any help is appreciated
The order of XML attributes is not important so I wouldn't worry about it. However, if it's really bugging you I would suggest moving your using statement.
using (StreamWriter sw = File.AppendText(#"C:\Users\status.xml"))
{
sw.WriteLine(contents);
}
Although this is probably optimized so that it works the same as if it were better written, the way you currently have this a new StreamWriter is being allocated and then disposed of with every iteration.
Your using statement should wrap the while loop and not the other way around. This could possibly solve the problem (though I think it's unlikely) as I don't know how the compiler is handling this line. Either way, it's worth changing.
Related
I would like to read file content and make some replacements on it. After that, the initial file with the replacements is loaded as XDocument. I have made a 2 different implementations:
implementation1:
string contents1 = File.ReadAllText(fileInfo.FullName, new UTF8Encoding(true));
File.WriteAllText(fileInfo.FullName, methodForReplacements(contents1), new UTF8Encoding(true));
return XDocument.Load(fileInfo.FullName, LoadOptions.PreserveWhitespace);
implementation2:
string contents;
using (FileStream fs = File.OpenRead(fileInfo.FullName))
{
using (StreamReader sr = new StreamReader(fs, new UTF8Encoding(true)))
{
contents = methodForReplacements(sr.ReadToEnd());
}
}
using (StreamWriter sw = new StreamWriter(fileInfo.Name, false, new UTF8Encoding(true)))
{
sw.Write(contents);
}
return XDocument.Load(fileInfo.FullName, LoadOptions.PreserveWhitespace);
replacementMethod():
private string methodForReplacements(string contents)
{
string replaced = new StringBuilder(contents)
.Replace("\r", "
")
.Replace("\n", "
")
.ToString();
return replaced;
}
After some benchmarking (10000 iterations, filesize: 265KB, numberOfReplacements: 10), it seems that the 2 implementations take very much the same time to execute (implementation1: 99sec, implementation2: 97sec). Is there any other more optimized and efficient way to achieve the same output?
It looks like a three task block process. so as long as you don't need to read ahead to know what you have to inert into the XDocument, what you can do is as you read in each line of input file, look for characters that need to be replaced, and then write out input line with changes. Between the input line and changed content, I gather you have enough information and content to decide what needs to go to the XDocument to respond accordingly. That'll save you some time.
I am trying to modify a file-stream inline as the file has the potential to be very large and I don't want to load it into memory. The piece of information I'm editing will always be the same length so in theory I can just swap the content out using a stream reader but it doesn't seem to be writing to the correct place
I have created a section of code that using a stream reader will read line by line until it finds a regex match and will then attempt to swap the bytes out with the edited line. The code is as follows:
private void UpdateFile(string newValue, string path, string pattern)
{
var regex = new Regex(pattern, RegexOptions.IgnoreCase);
int index = 0;
string line = "";
using (var fileStream = File.OpenRead(path))
using (var streamReader = new StreamReader(fileStream, Encoding.Default, true, 128))
{
while ((line = streamReader.ReadLine()) != null)
{
if (regex.Match(line).Success)
{
break;
}
index += Encoding.Default.GetBytes(line).Length;
}
}
if (line != null)
{
using (Stream stream = File.Open(path, FileMode.Open))
{
stream.Position = index + 1;
var newLine = regex.Replace(line, newValue);
var oldBytes = Encoding.Default.GetBytes(line);
var newBytes = Encoding.Default.GetBytes("\n" + newLine);
stream.Write(newBytes, 0, newBytes.Length);
}
}
}
The code almost works as expected, it inserts the updated line but it always does it a little early, just how early varies slightly based on the file I'm editing. I expect it is something to do with the way I am managing the stream position but I don't know the correct way to approach this.
Unfortunately the exact files I'm working on are under NDA.
The structure is as follows though:
A file will have an unkown amount of data followed by a line of a known format, for example:
Description: ABCDEF
I know the portion that follows "Description: " will always be 6 characters, so I do a replace on the line to replace with, for example, UVWXYZ.
The problem is that for example if a file read as
'...
UNIMPORTANT UNKNOWN DATA
DESCRIPTION: ABCDEF
MORE DATA
...'
it will come out as something like
'...
UNIMPORTANT UNKNOWN DDESCRIPTION: UVWXYZDEF
MORE DATA
...'
I think the problem here is that you are not considering the line feed ("\n") for each line you are getting and therefore your index is incorrectly setting the position of your stream. Try the following code:
private void UpdateFile(string newValue, string path, string pattern)
{
var regex = new Regex(pattern, RegexOptions.IgnoreCase);
int index = 0;
string line = "";
using (var fileStream = File.OpenRead(path))
using (var streamReader = new StreamReader(fileStream, Encoding.Default, true, 128))
{
while ((line = streamReader.ReadLine()) != null)
{
if (regex.Match(line).Success)
{
break;
}
index += Encoding.ASCII.GetBytes(line + "\n").Length;
}
}
if (line != null)
{
using (Stream stream = File.Open(path, FileMode.Open))
{
stream.Position = index;
var newBytes = Encoding.Default.GetBytes(regex.Replace(line + "\n", newValue));
stream.Write(newBytes, 0, newBytes.Length);
}
}
}
In your example, you are "off" by 4 Characters. Not quite the common "off by one error", but close. But maybe a different pattern would help the most?
Programms nowadays rarely work "on the file" like that. There is just too much to go wrong, all the way to a power loss mid-process. Instead they:
create a empty new file at the same location. Often temporary named and hidden.
write the output to the new file
Once you are done and eveyrthing is good - all the caches are flushed and everything is on the disk (done by Stream.Close() or Dispose()) - just replace the old file with the new file using the OS move operation.
The advantage is that it is impossible to have data-loss. Even if the computer looses power mid-operation, at tops the temporary file is messed up. You still got the orignal file and yoou can just delte the temporary file and restart the work from scratch if you need too. Indeed recovery only makes sense in rare cases (Word Processors)
The replacement of old file by new file is done with a move order. If they are on the same partition, that is literally just a rename operation in the Filesytem. And as modern FS are basically designed like a topline, robust relational Databases there is no danger in this.
You can find that pattern in everything from your Word Porcessor of choice, to backup programms, the download manager of Firefox (as you might be overriding a file that was there befroe) and even zipping programms. Everytime you got a long writing phase and want to minimize the danger, it is to go to pattern.
And as you can work entirely in memory without having to deal with moving around the read/write head, it will get around your issue too.
Edit: I made some source code for it from memory/documentation. Might contain syntax errors
string sourcepath; //containts the source file path, set by other code
string temppath; //containts teh path of the tempfile. Should be in the same folder, and thus same partiion
//Open both Streams, can use a single using for this
//The supression of any Buffering on the output should be optional and will be detrimental to performance
using(var sourceStream = File.OpenRead(sourcepath),
outStream = File.Create(temppath, 0, FileOptions.WriteThrough )){
string line = "";
//itterte over the input
while((line = streamReader.ReadLine()) != null){
//do processing on line here
outStream.Write(line);
}
}
//replace the files. Pretty sure it will just overwrite without asking
File.Move(temppath, sourcepath);
The code running on wince 5.0 / .net framework compact 2.0
Always get a exception says:
the process cannot access the file because it is being used by another process.
Really confused as i already encolse the stream in the using statement,so the filestream should be closed automaticly once leave the using block .
//read text
using (StreamReader sr = File.OpenText(fname))
{
string line;
while ((line = sr.ReadLine()) != null)
{
// append into stringbuilder
sb.Append(line);
sb.Append("\n");
}
}
//write text, below code raise the exception.
//if i comment it and re-run the code,exception disappear
using (StreamWriter sw = File.CreateText(fname))
{
sw.Write(sb.ToString());
}
addition:i just want to update the file, read and write. any better way?
sorry guys, the issue is in my code and i confused you here as i dont share that code.
actually because i wrote this in the very beginning of the program
// f is the fileinfo which point to fname as well
string text = f.OpenText().ReadToEnd();
this created a streamreader, not being assigned to any varible, but it's in the heap.so i ignored.
thanks people helpping here. BTW changed code to this then issue gone
using (StreamReader sr = f.OpenText())
{
string text = sr.ReadToEnd();
}
I tested this code on my computer. There is no problem.
Better way. For read and write full file, you can use File.ReadAllText(fname) and File.WriteAllText(fname). And instead of using \n use Environment.NewLine
I'm trying to get my program to read code from a .txt and then read it back to me, but for some reason, it crashes the program when I compile. Could someone let me know what I'm doing wrong? Thanks! :)
using System;
using System.IO;
public class Hello1
{
public static void Main()
{
string winDir=System.Environment.GetEnvironmentVariable("windir");
StreamReader reader=new StreamReader(winDir + "\\Name.txt");
try {
do {
Console.WriteLine(reader.ReadLine());
}
while(reader.Peek() != -1);
}
catch
{
Console.WriteLine("File is empty");
}
finally
{
reader.Close();
}
Console.ReadLine();
}
}
I don't like your solution for two simple reasons:
1)I don't like gotta Cath 'em all(try catch). For avoing check if the file exist using System.IO.File.Exist("YourPath")
2)Using this code you haven't dispose the streamreader. For avoing this is better use the using constructor like this: using(StreamReader sr=new StreamReader(path)){ //Your code}
Usage example:
string path="filePath";
if (System.IO.File.Exists(path))
using (System.IO.StreamReader sr = new System.IO.StreamReader(path))
{
while (sr.Peek() > -1)
Console.WriteLine(sr.ReadLine());
}
else
Console.WriteLine("The file not exist!");
If your file is located in the same folder as the .exe, all you need to do is StreamReader reader = new StreamReader("File.txt");
Otherwise, where File.txt is, put the full path to the file. Personally, I think it's easier if they are in the same location.
From there, it's as simple as Console.WriteLine(reader.ReadLine());
If you want to read all lines and display all at once, you could do a for loop:
for (int i = 0; i < lineAmount; i++)
{
Console.WriteLine(reader.ReadLine());
}
Use the code below if you want the result as a string instead of an array.
File.ReadAllText(Path.Combine(winDir, "Name.txt"));
Why not use System.IO.File.ReadAllLines(winDir + "\Name.txt")
If all you're trying to do is display this as output in the console, you could do that pretty compactly:
private static string winDir = Environment.GetEnvironmentVariable("windir");
static void Main(string[] args)
{
Console.Write(File.ReadAllText(Path.Combine(winDir, "Name.txt")));
Console.Read();
}
using(var fs = new FileStream(winDir + "\\Name.txt", FileMode.Open, FileAccess.Read))
{
using(var reader = new StreamReader(fs))
{
// your code
}
}
The .NET framework has a variety of ways to read a text file. Each have pros and cons... lets go through two.
The first, is one that many of the other answers are recommending:
String allTxt = File.ReadAllText(Path.Combine(winDir, "Name.txt"));
This will read the entire file into a single String. It will be quick and painless. It comes with a risk though... If the file is large enough, you may run out of memory. Even if you can store the entire thing into memory, it may be large enough that you will have paging, and will make your software run quite slowly. The next option addresses this.
The second solution allows you to work with one line at a time and not load the entire file into memory:
foreach(String line in File.ReadLines(Path.Combine(winDir, "Name.txt")))
// Do Work with the single line.
Console.WriteLine(line);
This solution may take a little longer for files because it's going to do work MORE OFTEN with the contents of the file... however, it will prevent awkward memory errors.
I tend to go with the second solution, but only because I'm paranoid about loading huge Strings into memory.
So I'm doing a project where I am reading in a config file. The config file is just a list of string like "D 1 1", "C 2 2", etc. Now I haven't ever done a read/write in C# so I looked it up online expecting to find some sort of rendition of C/C++ .eof(). I couldn't find one.
So what I have is...
TextReader tr = new StreamReader("/mypath");
Of all the examples online of how I found to read to the end of a file the two examples that kept occurring were
while ((line = tr.ReadLine() != null)
or
while (tr.Peek() >= 0)
I noticed that StreamReader has a bool EndOfStream but no one was suggesting it which led me to believe something was wrong with that solution. I ended up trying it like this...
while (!(tr as StreamReader).EndOfStream)
and it seems to work just fine.
So I guess my question is would I experience issues with casting a TextReader as a StreamReader and checking EndOfStream?
One obvious downside is that it makes your code StreamReader specific. Given that you can easily write the code using just TextReader, why not do so? That way if you need to use a StringReader (or something similar) for unit tests etc, there won't be any difficulties.
Personally I always use the "read a line until it's null" approach - sometimes via an extension method so that I can use
foreach (string line in reader.EnumerateLines())
{
}
EnumerateLines would then be an extension method on TextReader using an iterator block. (This means you can also use it for LINQ etc easily.)
Or you could use ReadAllLines, to simplify your code:
http://msdn.microsoft.com/en-us/library/s2tte0y1.aspx
This way, you let .NET take care of all the EOF/EOL management, and you focus on your content.
No you wont experience any issue's. If you look at the implementation if EndToStream, you'll find that it just checks if there is still data in the buffer and if not, if it can read more data from the underlying stream:
public bool EndOfStream
{
get
{
if (this.stream == null)
{
__Error.ReaderClosed();
}
if (this.charPos < this.charLen)
{
return false;
}
int num = this.ReadBuffer();
return num == 0;
}
}
Ofcourse casting in your code like that makes it dependend on StreamReader being the actual type of your reader which isn't pretty to begin with.
Maybe read it all into a string and then parse it: StreamReader.ReadToEnd()
using (StreamReader sr = new StreamReader(path))
{
//This allows you to do one Read operation.
string contents = sr.ReadToEnd());
}
Well, StreamReader is a specialisation of TextReader, in the sense that StreamReader inherits from TextReader. So there shouldn't be a problem. :)
var arpStream = ExecuteCommandLine(cmd, arg);
arpStream.ReadLine(); // Read entries
while (!arpStream.EndOfStream)
{
var line1 = arpStream.ReadLine().Trim();
// TeststandInt.SendLogPrint(line, true);
}