Write from a stream to a string - c#

I'm trying to use the streamwriter to write into a file that is created temporarily i.e. _logFileName and at the same time write the data written into the file to a string using stream reader. The current code shows no errors but at runtime says that it can not read from _logFileName as it is in use already.
how to do i do this ?
using (StreamWriter _logFile = File.CreateText(_logFileName))
{
//string s = "";
//using (StreamReader fill_log = new StreamReader(s))
using (StreamReader fill_log = new StreamReader(_logFileName))
{
_logFile.WriteLine("Logfile name is: " + _logFileName);
content += fill_log.ReadLine();
_logFile.WriteLine("LOG FILE STARTED AT: " + _startDateTime.ToString());
content += fill_log.ReadLine();
_logFile.WriteLine("============================================");
content += fill_log.ReadLine();
_logFile.Write(_message);
content += fill_log.ReadLine();
_logFile.WriteLine();
content += fill_log.ReadLine();
}
_logFile.Close();
}
So based on the suggestion i changed the code to this:
using (var fsWrite = new FileStream(_logFileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
using (var _logFile = new StreamWriter(fsWrite))
using (var fsRead = new FileStream(_logFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var fill_log = new StreamReader(fsRead))
{
_logFile.WriteLine();
content += fill_log.ReadLine();
_logFile.WriteLine("TIME OF LOG ENTRY: " + DateTime.Now);
content += fill_log.ReadLine();
// Arbitrary objects can also be written to the file.
_logFile.WriteLine(_message);
content += fill_log.ReadLine();
_logFile.Flush();
_logFile.Close();
On doing so, i am able to red and write simultaneously! that gave no problem. Thanks. But the content string variable seems to end after everyright. and ideas why this would happen ?

In order to be able to simultaneously read and write from the same file you have to create the FileStream object manually using one of the constructors that take a FileShare parameter, for example this one.
using (var fsWrite = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
using (var _logFile = new StreamWriter(fsWrite))
using (var fsRead = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var fill_log = new StreamReader(fsRead))
{
...
}

Another way to achieve what you want is using a specialized TextWriter that writes the StreamWriter and a StringBuilder:
using (StreamWriter _logFile = File.CreateText(_logFileName))
{
using (var builder = new StringBuildingStreamWriter(_logFile))
{
builder.WriteLine("Logfile name is: " + _logFileName);
builder.WriteLine("LOG FILE STARTED AT: " + _startDateTime.ToString());
builder.WriteLine("============================================");
builder.Write(_message);
builder.WriteLine();
content += builder.ToString();
}
_logFile.Close();
}
public class StringBuildingStreamWriter:TextWriter
{
StringBuilder sb = new StringBuilder();
private StreamWriter sw;
public StringBuildingStreamWriter(StreamWriter sw)
{
this.sw = sw;
}
public override void WriteLine(string value)
{
sb.AppendLine(value);
sw.WriteLine(value);
}
public override void WriteLine()
{
sw.WriteLine();
sb.AppendLine();
}
public override void Write(string value)
{
sb.Append(value);
sw.Write(value);
}
public override string ToString()
{
return sb.ToString();
}
public override Encoding Encoding
{
get { return UTF8Encoding.UTF8; }
}
}

Related

Value Being Overwritten When Reading From Text File

I am trying to create a list using the FileStream/StreamReader method. Everything works fine except the price calculation is reset every time a new line is added.
I believe the issue is with the save method. I am sure it is not caused from functions in my classes, since the price is showing properly. There seems to be an issue when saving the string.
This is my read method:
public static List<Customer> ReadCustomers()
{
// create an empty customer list
List<Customer> customerList = new List<Customer>();
// new Filestream
FileStream fs = null;
// new StreamReader
StreamReader sr = null;
Customer c; // for reading
string line;
string[] fields;
try
{
fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read);
sr = new StreamReader(fs);
while (!sr.EndOfStream)// while there is data
{
line = sr.ReadLine();
fields = line.Split(','); // split sections by commas
c = new Customer(); // initializes customer object
c.AccountNo = Convert.ToInt32(fields[0].Trim());
c.CustomerName = Convert.ToString(fields[1].Trim());
c.CustomerType = Convert.ToChar(fields[2].Trim());
c.CustomerCharge = Convert.ToDecimal(fields[3].Trim());
customerList.Add(c);
}
}
catch (Exception ex)
{
throw ex;
}
finally // always execute
{
if (fs != null) fs.Close(); // close file
}
return customerList;
}
This is where I try to save the string...
public static void SaveCustomers(List<Customer> list)
{
FileStream fs = null;
StreamWriter sw = null;
string line;
try
{
fs = new FileStream(path, FileMode.Create, FileAccess.Write);
sw = new StreamWriter(fs);
foreach (Customer c in list) // for each customer in the list
{
line = c.AccountNo.ToString() + ", " + c.CustomerName.ToString() + ", " +
c.CustomerType.ToString() + ", " + c.CustomerCharge.ToString(); // make a line with data
sw.WriteLine(line); // and write it to the file
}
}
catch(Exception ex)
{
throw ex;
}
finally
{
if (sw != null) sw.Close(); // stream writer close
if (fs != null) fs.Close();
}
}
Calculation:
public override decimal CalculateCharge()
{
decimal peak;
decimal offpeak;
if (Kwh1 <= INDUST_BASE_HOURS)
{
peak = KWH_PEAK_BASE_PRICE;
}
else
{
peak = ((Kwh1 - INDUST_BASE_HOURS) * KWH_INDUST_PEAK) + KWH_PEAK_BASE_PRICE;
}
if (Kwh2 <= INDUST_BASE_HOURS)
{
offpeak = KWH_OFF_PEAK_BASE_PRICE;
}
else
{
offpeak = ((Kwh2 - INDUST_BASE_HOURS) * KWH_INDUST_OFFPEAK) + KWH_OFF_PEAK_BASE_PRICE;
}
return peak + offpeak;
}
In SaveCustomers(), are you sure you want to open the file:
fs = new FileStream(path, FileMode.Create, FileAccess.Write);
You may want:
fs = new FileStream(path, FileMode.Append, FileAccess.Write);
FileMode.Create will destroy the file if it exists.
FileMode.Append will append to an existing file it exists.
Maybe for the purposes of clarity around testing, you output to another file rather than the one you read in.
Try using this by using the append parameter:
new StreamWriter("c:\\file.txt", true);
http://msdn.microsoft.com/en-us/library/36b035cb.aspx
Or you can see related answers here, which had similar problems
C# add text to text file without rewriting it?

Trying to serialize an object to a stream using Newtonsoft, getting an empty stream

I have an example of a program:
using System;
using Newtonsoft.Json;
using System.IO;
public class Program
{
public static void Main()
{
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var jsonWriter = new JsonTextWriter(writer))
{
new JsonSerializer().Serialize(jsonWriter, new { name = "Jamie" });
Console.WriteLine("stream length: " + stream.Length); // stream length: 0
Console.WriteLine("stream position: " + stream.Position); // stream position: 0
Console.WriteLine("stream contents: (" + reader.ReadToEnd() + ")"); // stream contents: ()
}
}
}
It should (according to this page: https://www.newtonsoft.com/json/help/html/SerializingJSON.htm) make a stream containing a JSON representation of the object: obj but in reality the stream appears to have length 0 and is an empty string when written out. What can I do to achieve the correct serialization?
Here is an example of the program running: https://dotnetfiddle.net/pi1bqE
You'll need to flush the JsonSerializer to make sure it's actually written data to the underlying stream. The stream will be at the end position so you'll need to rewind it back to the start position to read the data.
public static void Main()
{
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var jsonWriter = new JsonTextWriter(writer))
{
new JsonSerializer().Serialize(jsonWriter, new { name = "Jamie" });
jsonWriter.Flush();
stream.Position = 0;
Console.WriteLine("stream contents: (" + reader.ReadToEnd() + ")");
}
}
You need to flush your writer.
new JsonSerializer().Serialize(jsonWriter, new { name = "Jamie" });
jsonWriter.Flush();

How can I return a collection of large files without putting them in memory?

I need to write an API that retrieves a collection of file streams and then returns the files. These files can get large (~1GB). I need this to be as quick as possible, but this service can't consume too much memory.
In the past when I've had to do something like this, the files weren't large, so I just created a ZIP in memory and returned that. I'm not able to do that this time due to the memory constraint. From what I can tell, multipart responses don't exist, so I can't do that either. What options do I have? Is there some way I can stream zip back as a response?
public async Task GetFiles(string someId)
{
List<Stream> streamList = GetStreams(someId);
using (ZipArchive archive = new ZipArchive(responseStream /* ?? */, ZipArchiveMode.Create, true))
{
...
}
}
You could try using a gzipsteam, which avoids loading the files in memory.
https://msdn.microsoft.com/en-us/library/system.io.compression.gzipstream(v=vs.110).aspx
Here is the example from the page:
using System;
using System.IO;
using System.IO.Compression;
namespace zip
{
public class Program
{
private static string directoryPath = #"c:\temp";
public static void Main()
{
DirectoryInfo directorySelected = new DirectoryInfo(directoryPath);
Compress(directorySelected);
foreach (FileInfo fileToDecompress in directorySelected.GetFiles("*.gz"))
{
Decompress(fileToDecompress);
}
}
public static void Compress(DirectoryInfo directorySelected)
{
foreach (FileInfo fileToCompress in directorySelected.GetFiles())
{
using (FileStream originalFileStream = fileToCompress.OpenRead())
{
if ((File.GetAttributes(fileToCompress.FullName) &
FileAttributes.Hidden) != FileAttributes.Hidden & fileToCompress.Extension != ".gz")
{
using (FileStream compressedFileStream = File.Create(fileToCompress.FullName + ".gz"))
{
using (GZipStream compressionStream = new GZipStream(compressedFileStream,
CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
}
FileInfo info = new FileInfo(directoryPath + "\\" + fileToCompress.Name + ".gz");
Console.WriteLine("Compressed {0} from {1} to {2} bytes.",
fileToCompress.Name, fileToCompress.Length.ToString(), info.Length.ToString());
}
}
}
}
public static void Decompress(FileInfo fileToDecompress)
{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName = currentFileName.Remove(currentFileName.Length - fileToDecompress.Extension.Length);
using (FileStream decompressedFileStream = File.Create(newFileName))
{
using (GZipStream decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
Console.WriteLine("Decompressed: {0}", fileToDecompress.Name);
}
}
}
}
}
}

GZipStream delivers zero byte file

I am using DotNetZip's GZipStream to zip a file. The problem I have is that the resulting file is empty. I tried flushing/closing streams, but without result. Anyone knows what I do wrong:
using (var outputStream = new FileStream(path + fileName + ".gz", FileMode.Create, FileAccess.Write, FileShare.None))
{
using (var zipStream = new GZipStream(outputStream, CompressionMode.Compress))
{
using (var inputStream = new FileStream(path + fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
await inputStream.CopyToAsync(zipStream);
}
}
}
Works fine here; do you have a fully reproducible example, perhaps based on this one?
Results:
dummy.txt:6492 bytes
Waiting for completion (don't do this in real code, ever)...
Complete
dummy.txt.gz:512 bytes
Code:
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
static class P
{
static void Main()
{
File.WriteAllLines("dummy.txt",
Enumerable.Range(1, 200).Select(i => "this is some contents: line " + i));
WriteSize("dummy.txt");
var t = Task.Run(() => DoTheThing(Environment.CurrentDirectory + "\\", "dummy.txt"));
Console.WriteLine("Waiting for completion (don't do this in real code, ever)...");
t.Wait();
Console.WriteLine("Complete");
WriteSize("dummy.txt.gz");
}
private static void WriteSize(string path)
{
var file = new FileInfo(path);
Console.WriteLine(path + ":" + file.Length + " bytes");
}
async static Task DoTheThing(string path, string fileName)
{
using (var outputStream = new FileStream(path + fileName + ".gz", FileMode.Create, FileAccess.Write, FileShare.None))
{
using (var zipStream = new GZipStream(outputStream, CompressionMode.Compress))
{
using (var inputStream = new FileStream(path + fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
await inputStream.CopyToAsync(zipStream);
}
}
}
}
}

How can I read a text file without locking it?

I have a windows service writes its log in a text file in a simple format.
Now, I'm going to create a small application to read the service's log and shows both the existing log and the added one as live view.
The problem is that the service locks the text file for adding the new lines and at the same time the viewer application locks the file for reading.
The Service Code:
void WriteInLog(string logFilePath, data)
{
File.AppendAllText(logFilePath,
string.Format("{0} : {1}\r\n", DateTime.Now, data));
}
The viewer Code:
int index = 0;
private void Form1_Load(object sender, EventArgs e)
{
try
{
using (StreamReader sr = new StreamReader(logFilePath))
{
while (sr.Peek() >= 0) // reading the old data
{
AddLineToGrid(sr.ReadLine());
index++;
}
sr.Close();
}
timer1.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
using (StreamReader sr = new StreamReader(logFilePath))
{
// skipping the old data, it has read in the Form1_Load event handler
for (int i = 0; i < index ; i++)
sr.ReadLine();
while (sr.Peek() >= 0) // reading the live data if exists
{
string str = sr.ReadLine();
if (str != null)
{
AddLineToGrid(str);
index++;
}
}
sr.Close();
}
}
Is there any problem in my code in reading and writing way?
How to solve the problem?
You need to make sure that both the service and the reader open the log file non-exclusively. Try this:
For the service - the writer in your example - use a FileStream instance created as follows:
var outStream = new FileStream(logfileName, FileMode.Open,
FileAccess.Write, FileShare.ReadWrite);
For the reader use the same but change the file access:
var inStream = new FileStream(logfileName, FileMode.Open,
FileAccess.Read, FileShare.ReadWrite);
Also, since FileStream implements IDisposable make sure that in both cases you consider using a using statement, for example for the writer:
using(var outStream = ...)
{
// using outStream here
...
}
Good luck!
Explicit set up the sharing mode while reading the text file.
using (FileStream fs = new FileStream(logFilePath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
{
using (StreamReader sr = new StreamReader(fs))
{
while (sr.Peek() >= 0) // reading the old data
{
AddLineToGrid(sr.ReadLine());
index++;
}
}
}
new StreamReader(File.Open(logFilePath,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite))
-> this doesn't lock the file.
The problem is when you are writing to the log you are exclusively locking the file down so your StreamReader won't be allowed to open it at all.
You need to try open the file in readonly mode.
using (FileStream fs = new FileStream("myLogFile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (StreamReader sr = new StreamReader(fs))
{
while (!fs.EndOfStream)
{
string line = fs.ReadLine();
// Your code here
}
}
}
I remember doing the same thing a couple of years ago. After some google queries i found this:
FileStream fs = new FileStream(#”c:\test.txt”,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite);
i.e. use the FileShare.ReadWrite attribute on FileStream().
(found on Balaji Ramesh's blog)
Have you tried copying the file, then reading it?
Just update the copy whenever big changes are made.
This method will help you to fastest read a text file and without locking it.
private string ReadFileAndFetchStringInSingleLine(string file)
{
StringBuilder sb;
try
{
sb = new StringBuilder();
using (FileStream fs = File.Open(file, FileMode.Open))
{
using (BufferedStream bs = new BufferedStream(fs))
{
using (StreamReader sr = new StreamReader(bs))
{
string str;
while ((str = sr.ReadLine()) != null)
{
sb.Append(str);
}
}
}
}
return sb.ToString();
}
catch (Exception ex)
{
return "";
}
}
Hope this method will help you.

Categories