4

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?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Abhimanyu Garg
  • 416
  • 4
  • 10
  • 4
    Then you should at least strictly define what is "opening the file" means. – Andrey Korneyev Sep 29 '14 at 11:43
  • 2
    There is a **very important difference** between "last 4 bytes" (title) and "last 4 characters" (body). Please clarify. Also: why don't you want to open the file? Note: opening the file doesn't mean *reading* all of the file. – Marc Gravell Sep 29 '14 at 11:56
  • 1
    I guess that the OP does not know what "opening the file" means in this context. They wanted him to think it over, and to ask exactly that question... – Alexander Sep 29 '14 at 12:10
  • 2
    Crazy idea, I know, but to get the best answers to your question, it is often necessary to respond to comments that are asking for clarification... – Marc Gravell Sep 29 '14 at 12:17
  • Thanks guys. Sorry about the late reply. Opening the file means FileMode.Open. This question was asked to me in a Microsoft interview. I answered that i have done it but using FileMode.Open and he said that there is a way to do without it. Not sure if he was tricking me... – Abhimanyu Garg Oct 01 '14 at 05:10
  • I know there is difference between deleting 'last 4 bytes' and 'last 4 characters' but my point was that if we can do either it would help. – Abhimanyu Garg Oct 01 '14 at 05:12

3 Answers3

3

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.

Faris Zacina
  • 14,056
  • 7
  • 62
  • 75
2

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 :)
ken2k
  • 48,145
  • 10
  • 116
  • 176
  • 2
    corrupted filesystems here we come ... but yes ... :) – DarkSquirrel42 Sep 29 '14 at 11:59
  • What is the risk of corruption by doing this ? – BlueTrin Sep 29 '14 at 12:04
  • 2
    @BlueTrin it bypasses the OS and file subsystem entirely, and means the implementation needs to know the exact details of the file system. A single mistake (either a bug, or because it used an unexpected sub-version of the file-system), and that area of the drive could end up unreadable until nuked. – Marc Gravell Sep 29 '14 at 12:10
  • 1
    @BlueTrin This is not recommended of course, the only purpose is to provide an answer that doesn't need _at all_ an access to the file. As Marc noted, it's pretty easy to corrupt the file system if you do not _exactly_ do the same thing the OS does when a file is saved to the disk. While it's actually easy to work with a FAT drive, NTFS is another story. If the file is compressed, etc., it'll become really hard to update a MFT record properly. – ken2k Sep 29 '14 at 12:14
  • NTFS is another *file store* even? bad pun intended :) – default Sep 29 '14 at 13:17
0

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Nobody knows what the OP means by "opening the file", but calling `File.Open` doesn't seem to answer the question IMHO... – ken2k Sep 29 '14 at 12:05
  • @ken2k I strongly suspect the OP means "without reading (all of) the file" - in which case, this should work – Marc Gravell Sep 29 '14 at 12:07
  • Thanks guys. I have tried the solutions you have provided and it works. Not sure if the interviewer was looking for the same method. Lets leave it to this. – Abhimanyu Garg Oct 01 '14 at 05:16
  • @AbhimanyuGarg well, theres the `SetEndOfFile` win32 API, but that still requires a handle to the file with the `GENERIC_WRITE` flag. I would be interested in what answer the interviewer was looking for. – Marc Gravell Oct 01 '14 at 08:58