I am using streamwriter to write a string into stream. Now when I access the data from the stream, it adds "\0\0\0" characters to end of the content. I have to append the stream contents, so it creates problem as I am not able to remove these characters by trim() or remove() or replace() methods.
Below is the code I am using:
FOR WRITING :
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
using (StreamWriter writer = new StreamWriter(stream, System.Text.Encoding.Unicode))
{
try
{
string[] files = System.IO.Directory.GetFiles(folderName, "*.*", System.IO.SearchOption.AllDirectories);
foreach (string str in files)
{
writer.WriteLine(str);
}
// writer.WriteLine(folderName);
}
catch (Exception ex)
{
Debug.WriteLine("Unable to write string. " + ex);
}
finally
{
mutex.ReleaseMutex();
mutex.WaitOne();
}
}
}
FOR READING :
StringBuilder sb = new StringBuilder();
string str = #"D:\Other Files\Test_Folder\New Text Document.txt";
using (var stream = mmf.CreateViewStream())
{
System.IO.StreamReader reader = new System.IO.StreamReader(stream);
sb.Append(reader.ReadToEnd());
sb.ToString().Trim('\0');
sb.Append("\n" + str);
}
How can I prevent this?
[UPDATES]
Writing
// Lock
bool mutexCreated;
Mutex mutex = new Mutex(true, fileName, out mutexCreated);
if (!mutexCreated)
mutex = new Mutex(true);
try
{
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
using (BinaryWriter writer = new BinaryWriter(stream))
{
try
{
string[] files = System.IO.Directory.GetFiles(folderName, "*.*", System.IO.SearchOption.AllDirectories);
foreach (string str in files)
{
writer.Write(str);
}
writer.Flush();
}
catch (Exception ex)
{
Debug.WriteLine("Unable to write string. " + ex);
}
finally
{
mutex.ReleaseMutex();
mutex.WaitOne();
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Unable to monitor memory file. " + ex);
}
Reading
StringBuilder sb = new StringBuilder();
string str = #"D:\Other Files\Test_Folder\New Text Document.txt";
try
{
using (var stream = mmf.CreateViewStream())
{
System.IO.BinaryReader reader = new System.IO.BinaryReader(stream);
sb.Append(reader.ReadString());
sb.Append("\n" + str);
}
using (var stream = mmf.CreateViewStream())
{
System.IO.BinaryWriter writer = new System.IO.BinaryWriter(stream);
writer.Write(sb.ToString());
}
using (var stream = mmf.CreateViewStream())
{
System.IO.BinaryReader reader = new System.IO.BinaryReader(stream);
Console.WriteLine(reader.ReadString());
}
}
catch (Exception ex)
{
Debug.WriteLine("Unable to monitor memory file. " + ex);
}
No '\0' are getting appended by StreamWriter. These are just the content of the memory-mapped file, stuff that was there before you started writing. The StreamReader needs an end-of-file indicator to know when the stop reading. There isn't any in an mmf beyond the size of the section. Like the 2nd argument you pass to MemoryMappedFile.CreateNew(string, long).
Or in other words, you created a mmf that's too large to fit the stream. Well, of course, you didn't have the time machine to guess how large to make it. You definitely need to do something about it, trimming the zeros isn't good enough. That goes wrong the second time you write a stream that's shorter. The reader will now still sees the bytes from the previous stream content and they won't be zero.
This is otherwise a common headache with mmfs, they are just chunks of memory and a stream is a very poor abstraction of that. One of the big reasons it took so long for mmfs to get supported by .NET, even though they are a very core OS feature. You need pointers to map a mmf and that's just not well supported in a managed language.
I don't see a good way to teach StreamReader new tricks in this case. Copying the bytes from the mmf into a MemoryStream would fix the problem but rather defeats the point of a mmf.
Consider using a pipe instead.
Your combination of a MMF and TextWriter/TextReader, especially ReadToEnd() is not a good match.
A textReader needs the EOF concept of the underlying file and a MMF just does not supply that in the same way. You will get your strings stuffed with \0\0... up to the capacity of the MMF.
As a possible fix:
collect the strings to write in a StringBuilder
use a BinaryWriter to write it as 1 string
read it back with a BinaryReader.
Another options is to use WriteLine/ReadLine and define some EOF marker (empty line or special string).
The BinaryWriter will prefix the string with a length-prefix so that the Reader knows when to stop.
Related
I have a filestream being read by a streamreader. I want to return the position of the stream to the previous line in the event of an IOException to try and read the same line again. I tried to just record the position of the stream before the line, and then seek to that point, but I must be misunderstanding or misusing it.
using (var fs = new FileStream("MyPath\\linetest.txt", FileMode.Open))
using (var sr = new StreamReader(fs))
{
Console.WriteLine(fs.CanSeek);
while (!sr.EndOfStream)
{
string line;
try
{
var streamPosition = fs.Position;
line = sr.ReadLine();
if (line.StartsWith("#"))
{
fs.Seek(streamPosition, SeekOrigin.Begin);
throw new IOException();
}
Console.WriteLine(line);
}
catch (IOException e)
{
Console.WriteLine(e.Message);
}
}
In case its not obvious, I'm throwing an exception when a line is read that starts with "#". only one line in my 7 line test file starts with #, so I would expect that line to be written in an infinite loop, however, when I execute this, it prints each line of the file, substituting the exception message for the line that starts with #. Any insight or help you guy and gals could offer would be appreciated!
The reason why your code goes through to the end of input file is that the position you take by fs.Position is the one up to which StreamReader reads from FileStream into StreamReader's buffer at that phase (Position A). This is different from the position up to which StreamReader has returned lines by sr.ReadLine() (Position B). Off course, Position A is always ahead of Position B.
StreamReader does not have a way to provide Position B. It is not impossible to acquire such physical position by accumulatively calculating from what ReadLine() returns every time, but think of a variety of encoding and EOL and BOM code. That'll be not easy to be accurate.
Instead, why don't you get logical position after the decode by StreamReader? I mean, keep the line number in this case.
var row = 0;
using (var fs = new FileStream("input/sample.txt", FileMode.Open)
using (var sr = new StreamReader(fs))
{
Console.WriteLine(fs.CanSeek);
while (!sr.EndOfStream)
{
try
{
var line = sr.ReadLine();
if (line.StartsWith("#"))
throw new IOException();
row++;
Console.WriteLine(line);
}
catch (IOException e)
{
Console.WriteLine(e.Message);
// Reset to the line where the exception happened
fs.Seek(0, SeekOrigin.Begin);
sr.DiscardBufferedData();
for (var i = 0; i < row; i++)
sr.ReadLine();
}
}
}
sr.DiscardBufferedData() in the catch block is for clearing up the buffer in StreamReader that I mentioned at the beginning. Keep it in mind that sr.DiscardBufferedData() slows performance, which is mentioned in here.
I don't get why you want to keep the file stream open and keep getting the same error result. Be aware that the program never get updates on the file while the stream is open. Therefore, I leave one more code, which can keep reading the same line until the line is corrected, provided that already-processed lines are never edited in terms of line break.
var row = 0;
while (true)
{
using (var fs = new FileStream("input/sample.txt", FileMode.Open))
using (var sr = new StreamReader(fs))
{
for (var i = 0; i < row; i++)
sr.ReadLine();
try
{
while (!sr.EndOfStream)
{
var line = sr.ReadLine();
if (line.StartsWith("#"))
throw new IOException();
row++;
Console.WriteLine(line);
}
}
catch (IOException e)
{
Console.WriteLine(e.Message);
}
if (sr.EndOfStream)
break;
}
}
However, in either way, repeatedly reading through from the beginning to the error line is not so good even if you concern performance. In that case, you should reconsider the design how you input data.
I'm trying to read and write to the same file in a way such that no other program can access the file in between:
FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
StreamReader sr = new StreamReader(fs);
StreamWriter sw = new StreamWriter(fs);
newString = sr.ReadToEnd() + "somethingNew";
sw.Write(newString);
fs.Close();
The file is never written to. If I debug I can see that the reader manages to fetch the contents of the file, but the writer does not seem to be able to write to the file. Nothing happens.
I've been looking at this question which seems to be the same as mine. However I'm not able to get it to work.
Just Flush your changes to file, Have sw.Flush(); before closing the stream. like:
string filePath = "test.txt";
FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
StreamReader sr = new StreamReader(fs);
StreamWriter sw = new StreamWriter(fs);
newString = sr.ReadToEnd() + "somethingNew";
sw.Write(newString);
sw.Flush(); //HERE
fs.Close();
You may see this post simultaneous read-write a file in C# (open multiple streams for reading and writing)
As mentioned above - just add the Flush() to force the data held in the stream to be written to the file. In a comment you mentioned that you had previously used a 'using' statement but this hadn't worked.
Briefly here's why:
A using statement automatically calls Flush() so you don't have
to.
When you dispose of a StreamReader (or StreamWriter) - like by using a 'using' statement - the inner stream object is also disposed and you lose the handle to the stream.
#EJS a simple static method that you can use to create a new file if it does not exist as well as write to the same file if it does exist
Simple usage
string path = #"C:\SomePath\Name.txt";
if (!System.IO.File.Exists(path))
{
WriteAndOrAppendText(path, "File Created");
}
else if (System.IO.File.Exists(path))
{
WriteAndOrAppendText(path, "New Boot.");
}
private static void WriteAndOrAppendText(string path, string strText)
{
if (!File.Exists(path))
{
using (StreamWriter fileStream = new StreamWriter(path, true))
{
fileStream.WriteLine(strText);
fileStream.Flush();
fileStream.Close();
}
}
else
{
using (StreamWriter fileStream2 = new StreamWriter(path, true))
{
fileStream2.WriteLine(strText);
fileStream2.Flush();
fileStream2.Close();
}
}
}
For being able to create a file, append to it, and read the data in it while still allowing an application to write to it, as I believe you are trying to do, here's a set up I created:
string path = #"C:\SomePath\MyLogFile.txt";
public static string Log(string Message)
{
try
{
if (File.Exists(path) == false)
File.Create(path).Close(); // need this .Close()!!!
logCounter++;
string logString = logCounter + " " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString() + ": " + Message + Environment.NewLine;
using (FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
{
using (StreamWriter sw = new StreamWriter(fs))
{
sw.Write(logString);
}
}
return logString; // only necessary so we can return an error in the Exception block
}
catch (Exception ex)
{
return "Logger: Cannot log data. " + ex.ToString();
}
}
It's actually required to use FileAccess.Write if you do FileMode.Append - instead of being able to use FileAccess.ReadWrite - but I found that didn't matter because whatever had been written would have been closed and flushed to the file, and I could still open the file and read it (it wouldn't be locked & blank) using these. I have sw.Write() because I have Environment.NewLine that I added into my logString, but I could've done sw.WriteLine() and removed that, if I had wanted to.
One caveat: File.Exists() has issues if the path is long - can't remember the limit, but just know that there is one, so don't put your file you're writing to several layers deep. Less is always better.
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 .
I am a bit new to files in C# and am having a problem. When reading from a file and copying to another, the last chunk of text is not being written. Below is my code:
StringBuilder sb = new StringBuilder(8192);
string fileName = "C:...rest of path...inputFile.txt";
string outputFile = "C:...rest of path...outputFile.txt";
using (StreamReader reader = File.OpenText(fileName))
{
char[] buffer = new char[8192];
while ((reader.ReadBlock(buffer, 0, buffer.Length)) != 0)
{
foreach (char c in buffer)
{
//do some function on char c...
sb.Append(c);
}
using (StreamWriter writer = File.CreateText(outputFile))
{
writer.Write(sb.ToString());
}
}
}
My aim was to read and write to a textfile in a buffered manner. Something that in Java I would achieve in the following manner:
public void encrypt(File inputFile, File outputFile) throws IOException
{
BufferedReader infromfile = null;
BufferedWriter outtofile = null;
try
{
String key = getKeyfromFile(keyFile);
if (key != null)
{
infromfile = new BufferedReader(new FileReader(inputFile));
outtofile = new BufferedWriter(new FileWriter(outputFile));
char[] buffer = new char[8192];
while ((infromfile.read(buffer, 0, buffer.length)) != -1)
{
String temptext = String.valueOf(buffer);
//some changes to temptext are done
outtofile.write(temptext);
}
}
}
catch (FileNotFoundException exc)
{
} // and all other possible exceptions
}
Could you help me identify the source of my problem?
If you think that there is possibly a better approach to achieve buffered i/o with text files, I would truly appreciate your suggestion.
There are a couple of "gotchas":
c can't be changed (it's the foreach iteration variable), you'll need to copy it in order to process before writing
you have to keep track of your buffer's size, ReadBlock fills it with characters which would make your output dirty
Changing your code like this looks like it works:
//extracted from your code
foreach (char c in buffer)
{
if (c == (char)0) break; //GOTCHA #2: maybe you don't want NULL (ascii 0) characters in your output
char d = c; //GOTCHA #1: you can't change 'c'
// d = SomeProcessingHere();
sb.Append(d);
}
Try this:
string fileName = #"";
string outputfile = #"";
StreamReader reader = File.OpenText(fileName);
string texto = reader.ReadToEnd();
StreamWriter writer = new StreamWriter(outputfile);
writer.Write(texto);
writer.Flush();
writer.Close();
Does this work for you?
using (StreamReader reader = File.OpenText(fileName))
{
char[] buffer = new char[8192];
bool eof = false;
while (!eof)
{
int numBytes = (reader.ReadBlock(buffer, 0, buffer.Length));
if (numBytes>0)
{
using (StreamWriter writer = File.CreateText(outputFile))
{
writer.Write(buffer, 0, numBytes);
}
} else {
eof = true;
}
}
}
You still have to take care of character encoding though!
If you dont care about carraign returns, you could use File.ReadAllText
This method opens a file, reads each line of the file, and then adds each line as an element of a string. It then closes the file. A line is defined as a sequence of characters followed by a carriage return ('\r'), a line feed ('\n'), or a carriage return immediately followed by a line feed. The resulting string does not contain the terminating carriage return and/or line feed.
StringBuilder sb = new StringBuilder(8192);
string fileName = "C:...rest of path...inputFile.txt";
string outputFile = "C:...rest of path...outputFile.txt";
// Open the file to read from.
string readText = File.ReadAllText(fileName );
foreach (char c in readText)
{
// do something to c
sb.Append(new_c);
}
// This text is added only once to the file, overwrite it if it exists
File.WriteAllText(outputFile, sb.ToString());
Unless I'm missing something, it appears that your issue is that you're overwriting the existing contents of your output file on each blockread iteration.
You call:
using (StreamWriter writer = File.CreateText(outputFile))
{
writer.Write(sb.ToString());
}
for every ReadBlock iteration. The output of the file would only be the last chunk of data that was read.
From MSDN documentation on File.CreateText:
If the file specified by path does not exist, it is created. If the
file does exist, its contents are overwritten.
I have this code:
string s = "آ";
StreamWriter writer = new StreamWriter("a.txt", false, Encoding.UTF8);
writer.WriteLine(s);
but when I run it I can't see any "آ" in a.txt!! There isn't any string in a.txt! It is Empty! What is problem!?! Can anyone help me???
You never Close() the StreamWriter.
If you call writer.Close() when you finish writing, you will see the character.
But, since it implements IDisposable you should wrap the creation of the StreamWriter in a using statement:
using(StreamWriter writer = new StreamWriter("a.txt", false, Encoding.UTF8))
{
writer.WriteLine(s);
}
This will close the stream for you.
By the looks of it you're not Flush()ing or Close()ing the StreamWriter before you end your application. StreamWriter uses a buffer internally which needs to be flushed before you close your application or the StreamWriter goes out of scope otherwise the data you wrote to it will not be written to disc.
You can call Close() once you're done - although instead I would suggest using a using statement instead to also assure that your StreamWriter gets properly disposed.
string s = "آ";
using (StreamWriter writer = new StreamWriter("a.txt", false, Encoding.UTF8))
{
writer.WriteLine(s);
}
Try using File.WriteAllText("a.txt", s, Encoding.UTF8);.
Few tips:
do you see any character in the file written in place of one you expect? If not you're not flushing and closing the stream
StreamWriter should be able to write unicode without you having to choose encoding, but you could try to use UTF32 encoding.
Check How to: Write Text to a File
Follow, complete function for export a listview to excel with correct special characteres:
private void Exportar()
{
Encoding encoding = Encoding.UTF8;
saveFileDialog1.Filter = "Arquivo Excel (*.xls)|*.xls";
saveFileDialog1.FileName = "logs";
saveFileDialog1.Title = "Exportar para Excel";
StringBuilder sb = new StringBuilder();
foreach (ColumnHeader ch in lstPesquisa.Columns)
{
sb.Append(ch.Text + "\t");
}
sb.AppendLine();
foreach (ListViewItem lvi in lstPesquisa.Items)
{
foreach (ListViewItem.ListViewSubItem lvs in lvi.SubItems)
{
if (lvs.Text.Trim() == string.Empty)
{
sb.Append(" ");
}
else
{
string ITEM = Regex.Replace(lvs.Text, #"\t|\n|\r", "");//remover novas linhas
sb.Append(ITEM + "\t");
}
}
sb.AppendLine();
}
DialogResult dr = saveFileDialog1.ShowDialog();
if (dr == DialogResult.OK)
{
StreamWriter sw = new StreamWriter(saveFileDialog1.FileName, false, Encoding.UTF32);
sw.Write(sb.ToString());
sw.Close();
}
}
If you want to read a file from somewhere that contains these Unicode characters then you do some modifications and write back to the file, the problem arises. I was stuck with the same problem. Here is the solution that worked for me
Reading data from a file that contains Unicode characters
List<string>fileData= File.ReadAllLines("URPath",Encoding.Default).ToList());
//Any modifications
File.WriteAllLines("URPath", hstFileData,Encoding.Default);
I know this is not pretty but it worked.
hope this helps!