This is a question which was asked to me in an interview and still could not find a way to do it-
Suppose I have a .txt file and I want to delete the last 4 characters from the content of that file without opening the file. The first question is- Is it really doable? If yes, what is the way to do it?
I guess you can't read the content of the file. So if you can "open" it with write only access you could do:
using (var fileStream = File.Open("initDoc.txt", FileMode.Open, FileAccess.Write))
{
fileStream.SetLength(fileStream.Length - 4);
}
Of course you would need additional checks to make sure you are subtracting the correct number of bytes depending on the encoding, not subtracting more than the length etc.
If you can't use FileMode.Open, you can use an overload of the FileStream constructor that uses a SafeFileHandle. To acquire a SafeFileHandle to a file, you need to use C# Interop. In this example below i have wrapped the interop code to get a file handle in a class called "UnmanagedFileLoader":
var unmanagedFileLoader = new UnmanagedFileLoader("initDoc.txt");
using (var fileStream = new FileStream(unmanagedFileLoader.Handle, FileAccess.Write))
{
fileStream.SetLength(fileStream.Length - 4);
}
The UnmanagedFileLoader internally uses the unmanaged CreateFile function to open an existing file with write permissions:
handleValue = CreateFile(Path, GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
For more info how to acquire a SafeFileHandle you can check out this link:
http://msdn.microsoft.com/en-us/library/microsoft.win32.safehandles.safefilehandle%28v=vs.110%29.aspx
If you want to skip the FileStream ways, the third way to do it would be to use StreamReader and StreamWriter, and then read a file with StreamReader without the last 4 bytes, and then write it using a StreamWriter. But i would still recommend using the FileStream examples above.
EDIT: I assume "opening the file" means "getting a handle to the file".
Sure it's possible :
Open a handle to the drive that contains the file
Get the file system type
Scan the content of the structure that contains information about all files: the MFT (for NTFS), the FAT records..etc.
Find the entry that corresponds to your file
Updates the entry (write) by subtracting 4 to the value that stores the "file size" information :)
If your concern is about reading all the data for a long file: that isn't necessary. If we assume you really do mean bytes, simply:
using (var file = File.Open(path, FileMode.Open, FileAccess.Write)) {
file.SetLength(file.Length - 4);
}
this does not read the contents of the file.
If you mean characters, then you need to think very carefully about the encoding - 4 characters is not necessarily 4 bytes.
Related
I'm trying to make a map for a game that I'm planning to create. The map should have two data files, and a picture file.
I want to put them together, to form a single file, and I only want to use the default libraries.
How can I do this, and still be able to separate them later?
A solution would be compression, but I couldn't find a way to compress multiple files using the gzipstreamer class.
You could use SharpZipLib to create a ZIP file.
Did you consider embedding the files as resources in the assembly (or in a separate assembly?)
A lot depends on the reasons why you want to group them.
Compression will cost time and CPU power.
I think you should consider embedding the resources in the assembly as Erno suggests.
But if you really want to pack them into a single file, you could do so by simply writing the length of each stream before the stream itself. You could then read the length byte and afterwards return the next length bytes as a Stream. Reading/writing with ugly methods below. The target stream could eventually be gzipped. Note that the naive methods below reads and writes the entire string to a single buffer and assumes that no file is larger than int.MaxValue.
But I would not recommend using just the standard libraries.
static void Append(Stream source, Stream target)
{
BinaryWriter writer = new BinaryWriter(target);
BinaryReader reader = new BinaryReader(source);
writer.Write((long)source.Length);
byte[] buffer = new byte[1024];
int read;
do
{
read = reader.Read(buffer, 0, buffer.Length);
writer.Write(buffer, 0, read);
}
while (read > 0);
writer.Flush();
}
static Stream ReadNextStream(Stream packed)
{
BinaryReader reader = new BinaryReader(packed);
int streamLength = (int)reader.ReadInt64();
MemoryStream result = new MemoryStream();
byte[] buffer = new byte[streamLength];
reader.Read(buffer, 0, buffer.Length);
BinaryWriter writer = new BinaryWriter(result);
writer.Write(buffer, 0, buffer.Length);
writer.Flush();
result.Seek(0, SeekOrigin.Begin);
return result;
}
Gzip compression only works on one file (it only ever has). You could try ZIP, 7-ZIP or some other archive format that allows multiple files. Alternately you can TAR the files together first, which was common practice for the compression scheme Gzip was invented to replace.
I had a simiar question a while ago here about saving 2 XML files in one file.
See my answer with code.
"I ended up writing my own Stream, which can be thought of as a multistream. It allows you to treat one stream as multiple streams in succession. i.e. pass a multistream to an xml parser (or anything else) and it'll read up to a marker, which says 'this is the end of the stream'. If you then pass that same stream to another xml parser, it'll read from that marker, to the next one or EOF"
Your basic usage would be:
Writing:
Open File Stream
Create MultiStream passing in File Stream in constructor
Write data file to multistream
Call write end of stream marker on multistream
Write 2nd data file to multistream
Call write end of stream marker on multistream
Save picture to multistream
Close multistream
Close file stream
Reading:
Open File Stream
Create MultiStream passing in File Stream in constructor
Read data file
Call advance to next stream on multistream
Read 2nd data file
Call advance to next stream on multistream
Read image (Image.FromStream() etc.)
Close multistream
Close file stream
Is there a way to calculate the checksum of a file that is readonly?
The only examples I have seen uses an algorithm like this
public string GetChecksum()
{
FileStream file = new FileStream(_filePath, FileMode.Open);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] retVal = md5.ComputeHash(file);
file.Close();
StringBuilder sb = new StringBuilder();
foreach (byte t in retVal)
{
sb.Append(retVal[1].ToString("x2"));
}
return sb.ToString();
}
You can open a file even if it's readonly.
It is not possible to generate a checksum without opening the file, since you can't read a file without opening it.
You should pass FileAccess.Read to open it as read-only.
Also, you should generate checksums using SHA512, not MD5.
According to the documentation, the FileStream constructor you are using opens the file for read/write. Use an overload that specifies FileAccess.Read.
The constructor is given read/write
access to the file, and it is opened
sharing Read access
You cannot generate a checksum without reading the entire file.
Generally, readonly files can be opened. There might be file or folder permissions that prevent a given user from opening the file.
Well, no. You have to read a file to do anything with what's in it. But you're opening with Generic access when you probably want FileStream(_filePath,FileAccess.Read,true,4096,true); to open it read-only. StreamReader will do this automatically.
I've been playing around with encrypting and decrypting files in VC# Express 2010.
All the tutorials and documentation I've seen require two FileStreams in order to encrypt the file - one for reading the unencrypted version, and the other for encrypting. When I actually wrote the code it kept throwing an error telling me it could not open the file because it was opened by another process at the output filestream.
I'm assuming that's because the file is opened by the input filestream. So that means I have to specify a different filename? So even after the operation is successful I'll now have the original unencrypted file in the directory and a separate encrypted version? Doesn't that defeat the point? Or am I doing something wrong here? My code is similar to this...
public string filename = "test.xml";
using (FileStream input = new FileStream(filename, FileMode.Open, FileAccess.Read))
using (FileStream output = new FileStram(filename, FileMode.Open, FileAccess.Write))
using (....all the crypto stream and transform stuff...)
{
...do the encryption....
}
You're right but it's not defeating the point. The (streaming) crypto APIs are intended to encrypt from Src to Dst. Think encrypting output while sending/receiving over a network etc. This keeps them simple, as they should be.
You complicate the issue by using the same file for Src and Dst. That is not totally impossible but like Copying a File over itself it needs some extra care.
Consider that in general, encrypting will increase the File size. So it is not safe to Encrypt a file in place. Decrypting might be, but I wouldn't risk it.
What you need is a Temp file and a rename action after completion.
In your example, you can't create a separate filestream for both input and output on the same file, but you can create a handle that will read and write. The FileAccess enum has the flags attribute, so you'd just say var handle = new FileStream(filename, FileAccess.Read | FileAccess.Write); The obvious downside to this is you are going to have data lost if your encryption doesn't complete successfully.
I recommend having a separate file for the output though, atleast that way you won't lose data if your program breaks unexpectedly. If the encryption completes successfully, then delete the original and rename the encrypted file with the original file name.
Use File.ReadAllBytes. Then those bytes post to your encryptor, must work.
There is another parameter where you can specify whether or not to allow another process to read or write to the file.
openFile is a string that represents the file name.
using (FileStream fileIn = new FileStream(openFile, FileMode.Open, FileAccess.Read, FileShare.Write))
using (FileStream fileOut = new FileStream(openFile, FileMode.Open, FileAccess.Write, FileShare.Open))
This way, you can read and write to the same file.
while (myfileStream.Position < fileLength)
{
fileIn.Read(buffer, 0, 51200);
buffer = encrypt(buffer);
fileOut.Write(buffer, 0, 51200);
}
While this is easy and you don't have to write to a temporary file or have to move/rename etc, this can be really dangerous because if the encryption breaks suddenly for some reason, you will lose data!
Also, the encrypt function is something I implemented. AesCryptoServiceProvider along with CryptoStream can be used :)
I am trying to use NVelocity templates in a .Net application: using a template to output results to a file. It all seems to work fine except for the fact that the output is never fully overwritten. If my file is 100 characters long and the template only renders 20 characters, the last 80 characters are never altered!
Code sample:
FileStream fileStream = new FileStream(outputPath, FileMode.OpenOrCreate, FileAccess.Write);
using (StreamWriter streamWriter = new StreamWriter(fileStream))
{
velocityEngine.MergeTemplate(templateName, Encoding.Default.WebName, velocityContext, streamWriter);
}
So if my template outputs AAAA and the file already contains BBBBBBBB then at the end, the file contains AAAABBBB at the end of the op.
Any clue how I can get it to fully overwrite the file? - e.g. in the above example the final output should be AAAA. Not too sure whether this is just pure stream-related stuff - but I haven't had this problem before with filestreams.
Happy to write a reset method, or just output to a memorystream and overwrite the file, but I would like to get it working like this if possible!
**EDIT:'' got it working by calling
fileStream.SetLength(0);
when I open the file. But would appreciate knowing if there was a better way!
I think the solution is to change the FileMode.OpenOrCreate to simply FileMode.Create in the first line
From the MSDN Article on System.IO.FileMode..
FileMode.Create
Specifies that the operating system should create a new file. If the file already exists, it will be overwritten.
FileMode.OpenOrCreate
Specifies that the operating system should open a file if it exists; otherwise, a new file should be created.
If you don't know, at open time, that you may be truncating the file, you can use the SetLength method on the Stream to truncate it.
http://msdn.microsoft.com/en-us/library/system.io.stream.setlength.aspx
For this to work, the Stream must be writable and seekable.
With the following file reading code:
using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
using (TextReader tr = new StreamReader(fileStream))
{
string fileContents = tr.ReadToEnd();
}
}
And the following file write code:
using (TextWriter tw = new StreamWriter(fileName))
{
tw.Write(fileContents);
tw.Close();
}
The following exception details are seen:
The process cannot access the file
'c:\temp\myfile.txt' because it is
being used by another process.
What is the best way of avoiding this? Does the reader need to retry upon receipt of the exception or is there some better way?
Note that the reader process is using a FileSystemWatcher to know when the file has changed.
Also note that, in this instance, I'm not looking for alternatives ways of sharing strings between the 2 processes.
You can open a file for writing and only lock write access, thereby allowing others to still read the file.
For example,
using (FileStream stream = new FileStream(#"C:\Myfile.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.Read))
{
// Do your writing here.
}
Other file access just opens the file for reading and not writing, and allows readwrite sharing.
using (FileStream stream = new FileStream(#"C:\Myfile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
// Does reading here.
}
If you want to ensure that readers will always read an up-to-date file, you will either need to use a locking file that indicates someone is writing to the file (though you may get a race condition if not carefully implemented) or make sure you block write-sharing when opening to read and handle the exception so you can try again until you get exclusive access.
If you create a named Mutex you can define the mutex in the writing application, and have the reading application wait until the mutex is released.
So in the notification process that is currently working with the FileSystemWatcher, simply check to see if you need to wait for the mutex, if you do, it will wait, then process.
Here is a VB example of a Mutex like this that I found, it should be easy enough to convert to C#.
Get your process to check the status of the file if it is being written to. You can do this by the presence of a lock file (i.e. the presence of this other file, which can be empty, prevents writing to the main file).
Even this is not failsafe however, as the two processes may create the lock file at the same time - but you can check for this before you commit the write.
If your process encounters a lock file then get it to simply sleep/wait and try again at a predefined interval in the future.
Is there any particular reason for opening the file with FileShare.None? That'll prevent the file from being opened by any other process.
FileShare.Write or FileShare.ReadWrite should allow the other process (subject to permissions) to open and write to the file while you are reading it, however you'll have to watch for the file changing underneath you while you read it - simply buffering the contents upon opening may help here.
All of these answers, however, are equally valid - the best solution depends on exactly what you're trying to do with the file: if it's important to read it while guaranteeing it doesn't change, then lock it and handle the subsequent exception in your writing code; if it's important to read and write to it at the same time, then change the FileShare constant.
You can use a Mutex object for this.
The reader and writer both need retry mechanisms. Also FileShare should be set to FileShare.read for the readers and FileShare.none for the writer. This should ensure that the readers don't read the file while writing is in progress.
The reader (excluding retry) becomes
using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (TextReader tr = new StreamReader(fileStream))
{
string fileContents = tr.ReadToEnd();
}
}
The writer (excluding retry) becomes:
FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
using (TextWriter tw = new StreamWriter(fileStream))
{
tw.Write(fileContents);
tw.Close();
}
Write to a temp file, when finished writing rename/move the file to the location and/or name that the reader is looking for.
The best thing to do, is to put an application protocol on top of a file transfer/ownership transfer mechanism. The "lock-file" mechanism is an old UNIX hack that has been around for ages. The best thing to do, is to just "hand" the file over to the reader. There are lots of ways to do this. You can create the file with a random file name, and then "give" that name to the reader. That would allow the writer to asynchronously write another file. Think of how the "web page" works. A web page has a "link" to more information in it, for images, scripts, external content etc. The server hands you that page, because it's a coherent view of the "resource" you want. Your browser then goes and gets the appropriate content, based on what the page description (the HTML file or other returned content), and then transfers what it needs.
This is the most resilient type of "sharing" mechanism to use. Write the file, share the name, move to the next file. The "sharing the name" part, is the atomic hand off that makes sure that both parties (the reader and the writer) agree that the content is "complete."