I have a function which has an input parameter of type "stream".
I want to read the content of this stream and compare to a content of a file.
The following has been done:
public bool func(Stream stream, string tempFilePath, string dstFilePath)
{
string[] File1 = File.ReadAllLines(tempFilePath);
stream.Seek(0, SeekOrigin.Begin);
int line = 0;
StreamReader reader = new StreamReader(stream);
string[] File2 = reader.ReadToEnd().Split(
new[] { "\r\n", "\r", "\n" },
StringSplitOptions.None
);
while (line < File1.Length)
{
if (astFile2[line] != astFile1[line])
break;
line++;
}
bool ret = line == astFile1.Length && line == astFile2.Length - 1;
if (ret )
{
using (var dstFile = new FileStream(dstFilePath, FileMode.Create, FileAccess.Write))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(dstFile );
}
}
return ret;
}
The problem is that I can't take the content of the variable "stream" anymore using the command:
"stream.CopyTo(dstFile ); "
The content is empty!
The "position" of the stream using the stream.Seek(0, SeekOrigin.Begin); is correct.
I have used the stream.Seek(0, SeekOrigin.Begin); command after the ReadToEnd() line, but it did not help.
I have used also the stream.Position = 0, and did not help too.
That why I think there should be no problem by position.
What is it missed in this code?
Why the ReadToEnd() command results in emptying the stream?
For test, I also did the following:
string[] File2 = reader.ReadToEnd().Split(
new[] { "\r\n", "\r", "\n" },
StringSplitOptions.None
);
stream.Position = 0;
string[] File3 = reader.ReadToEnd().Split(
new[] { "\r\n", "\r", "\n" },
StringSplitOptions.None
);
The content of "File3" is still empty!
It seems that you are not showing us the actual faulty code.
I have paraphrased your code to a compilable console app, which does NOT reproduce the problem:
using System.IO;
namespace Demo
{
public static class Program
{
static void Main()
{
string sourceFile = #"D:\tmp\source.txt";
string destFile = #"D:\tmp\dest.txt";
MemoryStream ms = new MemoryStream();
var data = File.ReadAllBytes(sourceFile);
ms.Write(data, 0, data.Length);
ms.Position = 0;
func(ms, sourceFile, destFile);
}
public static void func(Stream stream, string tempFilePath, string dstFilePath)
{
File.ReadAllLines(tempFilePath);
stream.Seek(0, SeekOrigin.Begin);
var reader = new StreamReader(stream);
reader.ReadToEnd();
stream.Position = 0;
using (var dstFile = new FileStream(dstFilePath, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(dstFile);
}
}
}
}
So I'm afraid that the answer to the question "what is missed in this code?" is "a reproducable problem".
It is crucial that you tell us the concrete type of the Stream that you are passing to func(). It is likely that is the cause of your problem - for example, it's a stream that can only be read once (although in that case, trying to change the stream position should really throw an UnsupportedOperationException).
I always make sure I call stream.Flush() before doing a ReadToEnd().
Related
I'm reading a binary file and writing to another file in CP 437 format by skipping few lines. But the output file size is increased than the original file and also data is corrupted. Any help to identify the issue.
StreamReader sStreamReader = new StreamReader(#"D:\Denesh\Input.txt");
string AllData = sStreamReader.ReadToEnd();
string[] rows = AllData.Split(",".ToCharArray());
FileStream fileStream = new FileStream(TransLog, FileMode.Open);
StreamReader streamReader = new StreamReader((Stream)fileStream, Encoding.GetEncoding(437));
StreamWriter streamWriter = new StreamWriter(outFile, false);
int num = 0;
int count = 0;
while (!streamReader.EndOfStream)
{
string tlogline = streamReader.ReadLine();
if (rows[count] == Convert.ToString(num))
{
++count;
}
else
{
++num;
streamWriter.WriteLine(tlogline, streamReader.CurrentEncoding);
}
}
fileStream.Close();
streamWriter.Close();
Adding filestream for streamwriter solves the issue. Thanks.
I am trying to write from a .csv file to a new file.
Every time StreamWriter writes, it writes to the first line of the new file. It then overwrites that line with the next string, and continues to do so until StreamReader reaches EndOfStream.
Has anybody ever experienced this? How did you overcome it?
This is my first solution outside of those required in by my school work. There is an unknown number of rows in the original file. Each row of the .csv file has only 17 columns. I need to write only three of them and in the order found in the code snippet below.
Before coding the StreamWriter I used Console.WriteLine() to make sure that each line was in the correct order.
Here is the code snippet:
{
string path = # "c:\directory\file.csv";
string newPath = # "c:\directory\newFile.csv"
using(FileStream fs = new FileStream(path, FileMode.Open))
{
using(StreamReader sr = new StreamReader(fs))
{
string line;
string[] columns;
while ((line = sr.ReadLine()) != null)
{
columns = line.Split(',');
using(FileStream aFStream = new FileStream(
newPath,
FileMode.OpenOrCreate,
FileAccess.ReadWrite))
using(StreamWriter sw = new StreamWriter(aFStream))
{
sw.WriteLine(columns[13] + ',' + columns[10] + ',' + columns[16]);
sw.Flush();
sw.WriteLine(sw.NewLine);
}
}
}
}
}
You should open the target in the same scope as you are opening the source instead of doing so in the loop which will cause you to overwrite the file every time with the FileMode option OpenOrCreate.
var path = #"c:\directory\file.csv";
var newPath = #"c:\directory\newFile.csv"
using(var sr = new StreamReader(new FileStream(path, FileMode.Open)))
using(var sw = new StreamWriter(new FileStream(newPath, FileMode.OpenOrCreate, FileAccess.ReadWrite)))
{
while(!sr.EndOfStream)
{
string line = sr.ReadLine();
var columns = line.Split(',');
sw.WriteLine(columns[13] + ',' + columns[10] + ',' + columns[16]);
sw.WriteLine(sw.NewLine);
}
sw.Flush();
}
I also hope you are sure about your CSV spacing as you are hard coding the positions in your code.
To correctly fix your code, you'll want to structure more:
public void CopyFileContentToLog()
{
var document = ReadByLine();
WriteToFile(document);
}
public IEnumerable<string> ReadByLine()
{
string line;
using(StreamReader reader = File.OpenText(...))
while ((line = reader.ReadLine()) != null)
yield return line;
}
public void WriteToFile(IEnumerable<string> contents)
{
using(StreamWriter writer = new StreamWriter(...))
{
foreach(var line in contents)
writer.WriteLine(line);
writer.Flush();
}
}
You could obviously tailor and make it a bit more flexible. But this should demonstrate and resolve some of the issues you have with your loop and streams.
First off, you are creating and closing a write stream to the same file for every single line. This means the file gets overwritten every line. You want to take your using block outside of the while loop; however, if you insist on opening and closing the write stream for every single line, then you need to use FileMode.Append
{
string path=#"c:\directory\file.csv";
string newPath=#"c:\directory\newFile.csv"
using(StreamReader sr = new StreamReader(new FileStream(path, FileMode.Open))) // no need for 2 usings
using (FileStream aFStream = new FileStream (newPath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
string line;
string[] columns;
{
while((line = sr.ReadLine()) != null)
{
columns = line.Split(',');
using (StreamWriter sw = new StreamWriter(aFStream))
{
sw.WriteLine(columns[13] + ',' + columns[10] + ',' + columns[16]);
sw.Flush();
sw.WriteLine(sw.NewLine);
}
}
}
}
}
I'm currently trying to read a file, modify a few placeholders within and then write the file into an output stream. As its the output stream for a page response in aspx.net I'm using the OutputStream.Write method there (the file is an attachment in the end).
Originally I had:
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
while (readBytes < fs.Length)
{
tmpReadBytes = fs.Read(bytes, 0, bytes.Length);
if (tmpReadBytes > 0)
{
readBytes += tmpReadBytes;
page.Response.OutputStream.Write(bytes, 0, tmpReadBytes);
}
}
}
After thinking things over I came up with the following:
foreach(string line in File.ReadLines(filename))
{
string modifiedLine = line.Replace("#PlaceHolder#", "NewValue");
byte[] modifiedByteArray = System.Text.Encoding.UTF8.GetBytes(modifiedLine);
page.Response.OutputStream.Write(modifiedByteArray, 0, modifiedByteArray.length);
}
But it looks inefficient especially with the conversions. So my question is: Is there any better way of doing this?
As note the file itself is not very big, it's an about 3-4 KB sized textfile.
You don't need to handle the bytes your self.
If you know the file is and always will be small,
this.Response.Write(File.ReadAllText("path").Replace("old", "new"));
otherwise
using (var stream = new FileStream("path", FileMode.Open))
{
using (var streamReader = new StreamReader(stream))
{
while (streamReader.Peek() != -1)
{
this.Response.Write(streamReader.ReadLine().Replace("old", "new"));
}
}
}
To get the lines in a string array:
string[] lines = File.ReadAllLines(file);
To alter the lines, use a loop.
for (int i = 0; i < lines.Length; i++)
{
lines[i] = lines[i].Replace("#PlaceHolder#", "NewValue");
}
And to save the new text, first create a string with all the lines.
string output = "";
foreach(string line in lines)
{
output+="\n"+line;
}
And then save the string to the file.
File.WriteAllText(file,output);
I am reading a text file through a web service. I am getting the data in chunks of byte[] data and reassembling it into a MemoryStream. Now I am trying to figure out how to display the text data in a textarea. Any recommendations on how to do this, I'm stuck?
Update
Here is the final solution that returns the contents of the text file as a string which is then displayed in the textarea.
public string DownloadFile(string filenamepath)
{
int offset = 0;
int chunkSize = 16000;
string filecontents = string.Empty;
using (var wsc = new WebServiceClient())
{
using (var fs = new MemoryStream())
{
while (true)
{
var buffer = wsc.DownloadFile(filenamepath, offset, chunkSize);
int read = buffer.Length;
if (read <= 0)
break;
fs.Seek(offset, SeekOrigin.Begin);
fs.Write(buffer, 0, read);
offset += read;
}
using (var reader = new StreamReader(fs))
{
reader.BaseStream.Seek(0, SeekOrigin.Begin);
filecontents = reader.ReadToEnd();
}
}
}
return filecontents;
}
Wrap a StreamReader around that MemoryStream and call the reader's ReadToEnd() method.
Make sure to set the reader's encoding accordingly.
Use:
MemoryStream stream = ... //Your memory stream here
using (var reader = new StreamReader(stream))
{
textArea.Text = reader.ReadToEnd();
}
I know this question has been asked before on Stackoverflow, but could not find an explanation.
When I try to read a string from a compressed byte array I get an empty string on the first attempt, on the second I succed and get the string.
Code example:
public static string Decompress(byte[] gzBuffer)
{
if (gzBuffer == null)
return null;
using (var ms = new MemoryStream(gzBuffer))
{
using (var decompress = new GZipStream(ms, CompressionMode.Decompress))
{
using (var sr = new StreamReader(decompress, Encoding.UTF8))
{
string ret = sr.ReadToEnd();
// this is the extra check that is needed !?
if (ret == "")
ret = sr.ReadToEnd();
return ret;
}
}
}
}
All suggestions are appreciated.
- Victor Cassel
I found the bug. It was as Michael suggested in the compression routine. I missed to call Close() on the GZipStream.
public static byte[] Compress(string text)
{
if (string.IsNullOrEmpty(text))
return null;
byte[] raw = Encoding.UTF8.GetBytes(text);
using (var ms = new MemoryStream())
{
using (var compress = new GZipStream (ms, CompressionMode.Compress))
{
compress.Write(raw, 0, raw.Length);
compress.Close();
return ms.ToArray();
}
}
}
What happened was that the data seemed to get saved in a bad state that required two calls to ReadToEnd() in the decompression routine later on to extract the same data. Very odd!
try adding ms.Position = 0 before string ret = sr.ReadToEnd();
Where is gzBuffer coming from? Did you also write the code that is producing the compressed data?
Perhaps the buffer data you have is invalid or somehow incomplete, or perhaps it consists of multiple deflate streams concatenated together.
I hope this helps.
For ByteArray:
static byte[] CompressToByte(string data)
{
MemoryStream outstream = new MemoryStream();
GZipStream compressionStream =
new GZipStream(outstream, CompressionMode.Compress, true);
StreamWriter writer = new StreamWriter(compressionStream);
writer.Write(data);
writer.Close();
return StreamToByte(outstream);
}
static string Decompress(byte[] data)
{
MemoryStream instream = new MemoryStream(data);
GZipStream compressionStream =
new GZipStream(instream, CompressionMode.Decompress);
StreamReader reader = new StreamReader(compressionStream);
string outtext = reader.ReadToEnd();
reader.Close();
return outtext;
}
public static byte[] StreamToByte(Stream stream)
{
stream.Position = 0;
byte[] buffer = new byte[128];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (!(read > 0))
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
You can replace if(!(read > 0)) with if(read <= 0).
For some reason if(read <= 0) isn't displayed corret above.
For Stream:
static Stream CompressToStream(string data)
{
MemoryStream outstream = new MemoryStream();
GZipStream compressionStream =
new GZipStream(outstream, CompressionMode.Compress, true);
StreamWriter writer = new StreamWriter(compressionStream);
writer.Write(data);
writer.Close();
return outstream;
}
static string Decompress(Stream data)
{
data.Position = 0;
GZipStream compressionStream =
new GZipStream(data, CompressionMode.Decompress);
StreamReader reader = new StreamReader(compressionStream);
string outtext = reader.ReadToEnd();
reader.Close();
return outtext;
}
The MSDN Page on the function mentions the following:
If the current method throws an OutOfMemoryException, the reader's position in the underlying Stream object is advanced by the number of characters the method was able to read, but the characters already read into the internal ReadLine buffer are discarded. If you manipulate the position of the underlying stream after reading data into the buffer, the position of the underlying stream might not match the position of the internal buffer. To reset the internal buffer, call the DiscardBufferedData method; however, this method slows performance and should be called only when absolutely necessary.
Perhaps try calling DiscardBufferedData() before your ReadToEnd() and see what it does (I know you aren't getting the exception, but it's all I can think of...)?