from file object to file name - c++

I wonder if we can get the file name including its path from the file object that we have created for the file name in C and in C++ respectively
FILE *fp = fopen(filename, mode); // in C
ofstream out(filename); // in C++
ifstream in(filename); // in C++
Thanks!

You can't, in general. The file may not ever have had a file name, as it may be standard input, output, or error, or a socket. The file may have also been deleted; on Unix at least, you can still read to or write from a file that has been deleted, as the process retains a reference to it so the underlying file itself is not deleted until the reference count goes to zero. There may also be more than one name for a file; you can have multiple hard links to a single file.
If you want to retain the information about where a file came from, I would suggest creating your own struct or class that consists of a filename and the file pointer or stream.

There is no portable way to retrieve the file name of a FILE* object. It may not even be associated with an actual file (e.g. a FILE pointer for stdout).

There is no portable way. However particular platforms sometimes have ways to do that.
In Windows, if you can get the file's HANDLE (like the one you get from ::CreateFile() ), you can get the path from that using something like ZwQueryInformationFile().
From a FILE *, you can get a (Unix-style) file id using _fileno(). Then call _get_oshandle() to get the HANDLE.
Not sure how to do that from an std::ofstream, but you can research that.
Not sure how to do that on other OSes but it may be possible.

Related

Retrieve information about an open file

Can I retrieve information about a file previously opened with fopen() using only the pointer it returned?
The reason I ask is that I am trying to write a RAII-style wrapper class for FILE *s, and I want to make it as general as possible, and one of the functions I imagined for it was a copy-like operation, that would take a FILE * as an argument, and create a new reference to the same file.
Under POSIX, I can create a duplicate of a file descriptor with dup()/dup2(), and even get how the file is being accessed with fnctl()'s F_GETFL operation. However, even if I do that to the underlying descriptor of a FILE *, it isn't enough for guessing properties such as if the stream is text or binary (under POSIX, there no real difference, but I want to be general), or its orientation towards char- or wchar_t-based text.
So, is there is a way of learning about the stream I'm about to create a wrapper for, how far can I go, and how should I do it?
Thank you for you attention.

C++ open file for writing only if does not exists

I would like to open a file for writing with the standard library, but the file open should fail if the file already exists.
From what I can read in the documentation, ofstream::open only allows appending or truncating.
I could of course try to open for reading to check if the file exists, and reopen for writing if it doesn't, but there is no guarantee that the file will not be created by another process inbetween.
Could someone confirm this is not possible in C++ with the standard library (std::iostream) or with the C functions (FILE* functions)
Since C11 (and thus also in C++17), for fopen you can use mode "x" — exclusive mode, see this:
File access mode flag "x" can optionally be appended to "w" or "w+"
specifiers. This flag forces the function to fail if the file exists,
instead of overwriting it.
There are no fstream ways of doing this, but std::fopen is as much C++ as std::sin.
If you absolutely must have an fstream object of this file and you need the atomic check, you should first call fopen then on success, fclose and fstream::open:
std::ofstream create_new_file_for_writing()
{
FILE* fp = nullptr;
std::string fname;
do {
fname = random_file_name();
fp = fopen(fname.c_str(), "wx");
} while(!fp);
// here the file is created and you "own" the filename
fclose(fp);
return std::ostream(fname);
}
In std::ofstream by itself, no. Opening a file for writing always creates a new file if it does not already exist. There is no option to change that behavior. Opening a file for reading fails if the file does not exist.
However, on Windows at least, the Win32 API CreateFile() function has a CREATE_NEW flag that fails to open the file if it already exists. On other platforms, there may be flags available for _fsopen() and fopen() do accomplish the same thing.
It is possible to attach a FILE* to a std::ofstream (or maybe this is just a Microsoft extension, I am not sure), and in Visual C++ a FILE* can be created for a HANDLE returned by CreateFile() by using _open_osfhandle() with _fdopen(). See this question for examples:
Can I use CreateFile, but force the handle into a std::ofstream?
Other compilers/platforms may provide similar extensions for initializing an std::ofstream, you will have to look around.

how a single file can be linked to two different streams at same time?

ifstream fin("test.txt");
ofstream fout ("test.txt");
The above 2 lines, if written in the same programme, does not produce any error or warning.
But how can we write and read into a same file at same time.
How does this works?
So, as comments say, the compiler as such doesn't REALLY know what you are trying to achieve (and there are certainly cases where you'd actually want to something similar to this, for example):
// With exceptions for file operations enabled:
try
{
ifstream fin("test.txt");
return true;
}
catch(...)
{
try
{
ofstream fout("test.txt");
}
catch(...)
{
cout << "Can't create file";
return false;
}
return true;
}
Now, that will (in a rather stupid and complicated way) check if the file exists or can be created. Would you like the compiler to complain about that too? The compiler would have to "understand" a lot of logic to follow when which file is used when, and see that both can't be used at the same (and yes, the compiler probably DOES understand that in this case, since we have a return immediately after the fin use - make the code a bit more complicated, and it won't).
Now, you can't read/write into the same file with the code:
ifstream fin("test.txt");
ofstream fout ("test.txt");
Depending on the OS (and the flags used in the layer below), you may be able to actually execute both of those lines without error - in Unix, files that are open remain open in the system, even if something else removes the file (which the second line will do), so you can read the "old" file, and write to a new one. That is of course not "read and write the same file at the same time", but reading one file and writing to another. And in most non-Unix filesystems, this will not work, since the OS will use the same directory entry for both calls, and open the existing file, then the second call will either make the file empty or fail (depending on what OS, and such).
The solution, if you really want to read and write the same file is to either:
use ios_base::in|ios_base::out as flags when you open the file.
use a temporary file for the output side, and rename the file when it's "done". Since most types of changes to text-files require that the file is "rewritten" [except for adding things to the end], this is often the preferred method. Then when the changes are done, rename the new file to the old one (with a remove of the original one first). This also means you always have at least one complete file (albeit with the wrong name) even if your program crashes.
Nothing in the C++ Standard precludes this - the request is just passed through to the Operating System, which may or may not allow it.
For example, on some OS/filesystems combinations if you go to create a file with the same name as an existing file, the OS hides the existing file's directory entry/entries so other apps can't open it, but existing processes that are using it will be able to continue to do so; when they all close the file will be deleted. Meanwhile, a new file can be created with the same name, and after that any apps opening the file will see whatever's been flushed to that new file.
On other systems, some kind of "in use" or "locked" error message may be generated, leading to a fail/bad state in the std::ofstream.
You should always test the success of file stream creation, e.g.:
if (std::ifstream in("filename.txt"))
...use in...
else
std::cerr << ... or throw std::runtime_error(...)
There is no reason how compiler can understand that you are doing something wrong, it will not remember all filenames you have ever used in your program. Moreover, if the filenames come from user input or simply from a variable, the compiler can never guess that they will be identical:
ifstream fin(s1);
ofstream fout (s2); // is s1==s2?
In fact, your case (hard-coded file name) is very unusual situation, so I do not think such a warning can be useful in a general case.
And, in addition to this all, this code can sometimes even work correctly.
For example, if test.txt is a named pipe, than you can safely open it simultaneously both for reading and writing. You will still have to do some workaround to prevent blocking, a simplest approach will be to open the pipe in different threads. But you can safely imagine an situation where named pipe operations will not block (I do not know whether this is possible in popular OSes by some OS option etc., but anyway you can always imagine such an OS), in such a case the following code should work without any problem:
// (for imaginary OS where named pipe operations do not block)
ifstream fin("test.fifo");
ofstream fout("test.fifo");
fout << 42;
int x;
fin >> x; // produces 42
So there is no reason for a compiler to warn or give an error. If you want to check in runtime whether the file open was successful, just check it.

What does ifstream::open() really do?

Consider this code:
ifstream filein;
filein.open("y.txt");
When I use the open() function, what happens?
Does the file stream itself get opened?
or does the object's state change to open?
or both?
It's not clear if you want to know implementation details or standard requirements - but as for implementation details - it will call the underlying open system call on the operating system. On Linux for example this is called open. On Windows it is called CreateFile.
The filestream being open or closed is represented by it's state. So if you change the state to open, the filestream is now open. Like a doorway. If you open it, you've changed it's state to the open position. Then you can later close it, which involves changing it's state to the closed position. Changing its state to open and opening the stream are the exact same thing.
The std::ifstream is set up to own a std::filebuf which is a class derived from std::streambuf. The stream buffer is managing buffering for streams in a generic way and abstracts the details of how a stream is accessed. For a std::filebuf the underlying stream is an operating system file accessed as needed. When std::ifstream::open() is called this call is mainly delegated to std::filebuf::open() which does the actual work. However, the std::ifstream will clear() its state bits if the call to std::filebuf::open() succeeds and set std::ios_base::failbit if the call fails. The file buffer will call the system's method to allocate a file handle and, if successful, arrange for this file handle to be released in its destructor or in the std::filebuf::close() function - whatever comes first. When calling std::ifstream::open() with the default arguments the system call will check that the file exists, is accessible, not too many file handles are open, etc. There is an std::ios_base::openmode parameter which can be used to modify the behavior in some ways and when different flags are used when calling std::ofstream::open().
Whether the call to std::filebuf::open() has any other effects is up to the implementation. For example, the implementation could choose to obtain a sequence of bytes and convert them into characters. Since the user can override certain setting, in particular the std::locale (see the std::streambuf::pubimbue() function), it is unlikely that much will happen prior to the first read, though. In any case, none of the state flags would be affected by the outcome of any operation after opening the file itself.
BTW, the mentioned classes are actually all templates (std::basic_ifstream, std::basic_filebuf, std::basic_streambuf, and std::basic_ofstream) which are typedef'ed to the names used above for the instantiation working on char as a character type. There are similar typedefs using a w prefix for instantiations working on wchar_t. Interestingly, there are no typedefs for the char16_t and char32_t versions and it seems it will be a bit of work to get them instantiated as well.
If you think logically, ifstream is just the stream in which we will get our file contents. The parameters, we provide to ifstream.open() will open the file and mark it as open. When the file is marked as open, it will not allow you to do some operations on file like renaming a file as it is opened by some program. It will allow you to do the same after you close the stream. ifstream - imo is only the helper class to access files.

Clearing Contents of a File in C++ knowing only the FILE *

Is it possible to clear the contents (ie. set EOF to the beginning/reset the file) in C++ knowing just the FILE*? I'm writing to a temp file with wb+ access and wish to sometimes clear it and truncate it without adding the calls to fclose and fopen. I dont think it's possible... but if not, why not?
Thanks in advance!
It will depend on your platform. The POSIX standard provides ftruncate(), which requires a file descriptor, not a FILE pointer, but it also provides fileno() to get the file descriptor from the FILE pointer.
The analogous facilities will be available in Windows environments - but under different names.
I don't believe this can be done using just the FILE*. You can always write null data through the end of the file but that won't truncate it.
Alternately if you have access to the filename (I can't tell from the question) you could use freopen which hides the close/open/truncate into a single function call.
#include <cstdio>
freopen(null, "w", filePtr);
see http://www.cplusplus.com/reference/clibrary/cstdio/freopen/ for more. espacialy the description for the parameter filename.