fopen - file and directories ??

F

fdm

I use fopen to check if a file or directory exists:

#include <stdio.h>
#include <string>

void file_exists(std::string file) {
FILE* fp = NULL;
fp = fopen( file.c_str(), "rb" );
if( fp == NULL ) {
std::cout << "File = " << file << " does not exist!" << std::endl;
exit(0);
}
fclose(fp);
}

On Ubuntu linux this works fine both for files and directories. But on
windows vista 64 using visual studio 2008 it only works for files. If 'file'
is eg:

"c:\test" // I have also tried with ''c:/test"

then I get the message:

File = c:/test/ does not exist!

which means that fp is NULL. Why does it work on linux for directories but
not on windows?
 
F

Francesco S. Carta

I use fopen to check if a file or directory exists:

#include <stdio.h>
#include <string>

void file_exists(std::string file) {
FILE* fp = NULL;
fp = fopen( file.c_str(), "rb" );
if( fp == NULL ) {
std::cout << "File = " << file << " does not exist!" << std::endl;
exit(0);
}
fclose(fp);

}

On Ubuntu linux this works fine both for files and directories. But on
windows vista 64 using visual studio 2008 it only works for files. If 'file'
is eg:

"c:\test" // I have also tried with ''c:/test"

then I get the message:

File = c:/test/ does not exist!

which means that fp is NULL. Why does it work on linux for directories but
not on windows?

Could you please kindly explain me why you go on asking further
questions without bothering to drop in your previous threads and
explain what your issues were and how you solved them?

You've been suggested to inspect the strings, you've been handed a
couple of utilities for doing that. Please use them and post the
results, there in that other thread.

Also please explain us why you are still using fopen in place of
filestreams (regarding both threads), and what kind of input you would
expect from opening and reading a /directory/ as a /file/ (regarding
this very thread).

You should also know what '\t' stands for. If you don't, look it up.
 
F

fdm

Francesco S. Carta said:
Could you please kindly explain me why you go on asking further
questions without bothering to drop in your previous threads and
explain what your issues were and how you solved them?

You've been suggested to inspect the strings, you've been handed a
couple of utilities for doing that. Please use them and post the
results, there in that other thread.

Also please explain us why you are still using fopen in place of
filestreams (regarding both threads), and what kind of input you would
expect from opening and reading a /directory/ as a /file/ (regarding
this very thread).

You should also know what '\t' stands for. If you don't, look it up.

--



That thread prety much went off track, so I found some other code that I
could use - ala properties for java. This works for files on both windows
and linux. But it seems that its not possible to write code that check if a
folder exists for both platforms.

http://www.daniweb.com/forums/thread128591.html
http://www.vbforums.com/showthread.php?t=501284

As you recommended I now use ifstream instead (works fine for files) :

#include <iostream>
#include <fstream>

// have tried all the below....
std::string file;
//file = "E:/test";
//file = "E:/test/";
//file = "E:\\test";
file = "E:\\test\\";


std::ifstream in(file.c_str());
if(!in) {
std::cout << "File = " << file << " does not exist!" << std::endl;
exit(0);
}


But this still does not work for folders on windows. A possibility is to use
a preprocessor

#ifdef WIN32

// do file IO the windows way

#else

// do file IO the linux way

#endif

Damn I miss java!
 
F

Francesco S. Carta

That thread prety much went off track

Lots of threads do - they just split in sub-threads. You just would
have had to post there again and close the main thread.

Notice also that people cannot reasonably know _why_ you decided not
to post replies to their suggestions - they can only notice that you
ignored them, while they took time and effort to help you.
, so I found some other code that I
could use - ala properties for java. This works for files on both windows
and linux. But it seems that its not possible to write code that check if a
folder exists for both platforms.

Ah, now your issue is clearer.
http://www.daniweb.com/forums/thread128591.htmlhttp://www.vbforums.com/showthread.php?t=501284

As you recommended I now use ifstream instead (works fine for files) :

#include <iostream>
#include <fstream>

// have tried all the below....
std::string  file;
//file =  "E:/test";
//file =  "E:/test/";
//file =  "E:\\test";
file =  "E:\\test\\";

  std::ifstream in(file.c_str());
  if(!in) {
    std::cout << "File = " << file << " does not exist!" << std::endl;
    exit(0);
  }

But this still does not work for folders on windows. A possibility is to use
a preprocessor

#ifdef WIN32

// do file IO the windows way

#else

// do file IO the linux way

#endif

Exactly: you're stepping into the realm of platform-dependent stuff -
file-system issues are outside of C++ worries.

Just a bit later, you'll want to check the time-stamp of a file or its
read/write flags. Just find the facilities that let you interrogate
the file-system on the different platforms, then use conditional
preprocessor directives to include them depending on the platform onto
which the code is getting compiled each time - just like you're
starting to do above. Possibly, hide all the above within a single
interface that you will then use in your program - where you shouldn't
be showing platform-dependent stuff.

Sorry for being harsh in my previous post, but ignoring the people who
strive to help you leans strongly towards offending them, and they
could - correctly and reasonably - decide to plainly ignore your
subsequent help requests.
 
F

fdm

message








That thread prety much went off track

Lots of threads do - they just split in sub-threads. You just would
have had to post there again and close the main thread.

Notice also that people cannot reasonably know _why_ you decided not
to post replies to their suggestions - they can only notice that you
ignored them, while they took time and effort to help you.
, so I found some other code that I
could use - ala properties for java. This works for files on both windows
and linux. But it seems that its not possible to write code that check if
a
folder exists for both platforms.

Ah, now your issue is clearer.
http://www.daniweb.com/forums/thread128591.htmlhttp://www.vbforums.com/showthread.php?t=501284

As you recommended I now use ifstream instead (works fine for files) :

#include <iostream>
#include <fstream>

// have tried all the below....
std::string file;
//file = "E:/test";
//file = "E:/test/";
//file = "E:\\test";
file = "E:\\test\\";

std::ifstream in(file.c_str());
if(!in) {
std::cout << "File = " << file << " does not exist!" << std::endl;
exit(0);
}

But this still does not work for folders on windows. A possibility is to
use
a preprocessor

#ifdef WIN32

// do file IO the windows way

#else

// do file IO the linux way

#endif

Exactly: you're stepping into the realm of platform-dependent stuff -
file-system issues are outside of C++ worries.




I have modfied this piece that the author claims to be "compatible with more
that windows":

http://www.tek-tips.com/viewthread.cfm?qid=960814&page=1

to this:

if ( !access( file.c_str(), 0 ) == 0 ) {
std::cout << "Path = " << file << " doesn't exist." << std::endl;
exit(0);
}

But when I run it on linux it cannot find io.h. So as you point out it seems
that its time to set up some preprocessor scopes.





Just a bit later, you'll want to check the time-stamp of a file or its
read/write flags. Just find the facilities that let you interrogate
the file-system on the different platforms, then use conditional
preprocessor directives to include them depending on the platform onto
which the code is getting compiled each time - just like you're
starting to do above. Possibly, hide all the above within a single
interface that you will then use in your program - where you shouldn't
be showing platform-dependent stuff.

Sorry for being harsh in my previous post, but ignoring the people who
strive to help you leans strongly towards offending them, and they
could - correctly and reasonably - decide to plainly ignore your
subsequent help requests.



I know but I basically had no idea on what you were talking about :)
 
F

Francesco S. Carta

I know but I basically had no idea on what you were talking about :)

Fine, now you know.

I take the chance to remember you to properly format your quoting,
otherwise it can become quite hard to understand who said what.

Have good time and good coding,
cheers,
Francesco
 
F

fdm

Francesco S. Carta said:
Fine, now you know.

I take the chance to remember you to properly format your quoting,
otherwise it can become quite hard to understand who said what.


Yes I know. I use Outlook Express. Normally when I press "Reply Group" the
">" are automatically inserted. But for some reason this is not always the
case (seems they have been insterted correctly in the above message). Its
pretty time consuming to insert them manually when they are missing.
 
F

Francesco S. Carta

Yes I know. I use Outlook Express. Normally when I press "Reply Group"   the
">" are automatically inserted. But for some reason this is not always the
case (seems they have been insterted correctly in the above message). Its
pretty time consuming to insert them manually when they are missing.

Maybe you just need to install OE Quotefix - just heard of, OE is not
my piece of cake.
 
J

James Kanze

I use fopen to check if a file or directory exists:
#include <stdio.h>
#include <string>
void file_exists(std::string file) {
FILE* fp = NULL;
fp = fopen( file.c_str(), "rb" );
if( fp == NULL ) {
std::cout << "File = " << file << " does not exist!" << std::endl;
exit(0);
}
fclose(fp);
}
On Ubuntu linux this works fine both for files and directories.

Maybe. I'm not sure about Ubuntu, but under Solaris, it only
works for directories on the local disk. The fopen fails on
remote mounted directories. (IIRC, at least---it's been a long
time since I tried it.)
But on windows vista 64 using visual studio 2008 it only works
for files. If 'file' is eg:
"c:\test" // I have also tried with ''c:/test"
then I get the message:
File = c:/test/ does not exist!
which means that fp is NULL. Why does it work on linux for
directories but not on windows?

Because Linux is a different OS than Windows. In fact, I
suspect that it depends on the file system type, more than the
OS, and that it doesn't work reliably for directories anywhere.
For anything which might involve directories, you have to revert
to system specific operations (something like access or stat under
Unix systems, or GetFileAttributes under Windows).
 
J

James Kanze

fdm said:
I have modfied this piece that the author claims to be
"compatible with more that windows": [...]
to this:
if ( !access( file.c_str(), 0 ) == 0 ) { [...]
But when I run it on linux it cannot find io.h. So as you
point out it
Right, on Linux/POSIX it is elsewhere, see man access

I'm not even sure that access is standard Windows. Although
Windows has wrappers for a lot of the Posix functions, there are
often subtle differences (Windows is not Posix), and I generally
prefer using the native functions. In this case: access is
often just a wrapper around stat in Unix implementations as
well, and I'll generally just use stat or GetFileAttributes
directly.

Note that at least under Posix (and probably under Unix as
well), this may fail even if the file or directory exists, if
for example you don't have adequate access rights to some lower
directory in the path. Which means that you'll probably have to
look at the cause of failure (errno, GetLastError) in case of
failure.
A more nasty difference is that in order to work correctly
with filenames containing all possible characters (umlauts,
hieroglyphs, etc), your code has to support filenames encoded
as UTF-8 in Linux (this is the common case) and as UTF-16 on
Windows, and you have to use different string type and a
different function (_waccess()) on Windows. So you just can't
call access() and be portable. A proper solution is that you
decide what internal encoding you are using in your program,
then declare your own internal function like
int my_access(const std::string& filename_utf8, int mode);
then implement this function differently in two separate
source files, with encoding conversions etc., one of which is
compiled only in Windows and the other only in Linux. The
Windows source file includes <io.h>, the Linux source file
includes some other header. The rest of the program just uses
my_access() instead of access().

You're right, of course, but you're opening up a real can of
worms. First of all, at least under Posix/Linux, the filename
isn't necessarily UTF-8; the system makes no requirements
concerning the filename except that the individual components in
the path do not contain a character encoded with 0x00 or 0x2F
('/' in ASCII, ISO 8859 and UTF-8); this does eliminate
encodings like UTF-16, but ISO 8859-1 is quite common, as is
UTF-8. And since the only place the encoding makes a difference
is when displaying the file names, it's quite possible to use
different encodings for different files, even in the same
directory. (Sort of makes the output of ls a bit wierd to look
at, but the system doesn't care.) Under Windows, of course, the
native encoding is (so I've heard) UTF-16LE, but when passed a
name encoded in a char[], it assumes the current code page to do
the conversion. It's restrictions are also expressed in the
form of "any character except"..., which leaves a lot open. And
of course, remotely mounted files (both under Unix and under
Windows) may introduce other restrictions. (See
http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
for a discussion of some of the issues under Windows.)
Yes, this is a lot of work to do, as this concerns all
filesystem related functions. I think boost::filesystem
library should solve such issues, but I have not used it
myself.

I doubt that it addresses the encoding issues, since there is
basically no general solution possible.
 
R

Ross A. Finlayson

I use fopen to check if a file or directory exists:

#include <stdio.h>
#include <string>

void file_exists(std::string file) {
    FILE* fp = NULL;
    fp = fopen( file.c_str(), "rb" );
    if( fp == NULL ) {
        std::cout << "File = " << file << " does not exist!" << std::endl;
        exit(0);
    }
    fclose(fp);

}

On Ubuntu linux this works fine both for files and directories. But on
windows vista 64 using visual studio 2008 it only works for files. If 'file'
is eg:

"c:\test"   // I have also tried with ''c:/test"

then I get the message:

      File = c:/test/ does not exist!

which means that fp is NULL. Why does it work on linux for directories but
not on windows?

Because in Linux or Unix the directories are actually files. On
Windows there's a DOS file attribute or so, the file system attribute.

So you can't open directories with fopen on Windows. On Windows,
getting the directory is null, because as a file it's null. (I could
be wrong, also it could be set up to return an open file handle if you
use your own API routines around the Windows API.) On Unix, getting
the directory is a file, with a list of the files in the directory.

On some Unix machines the directories have user and group attributes
so the directory access could be made null on Unix also.

In C++ it's called ::fopen(const char*, ...);, let's see, the second
argument to fopen is the collected open flags so it could be a byte,
probably it is a default numeric type the integer: int.

fopen(const char*, const char*);

Why it's ::fopen instead of fopen() is that then other C++ programs in
the same code could use fopen instead of ::fopen. In C++ it's
standard that ::fopen() is the same as fopen().

The "fopen" is for "File Open", many of the file access routines begin
with f in the file "stdio.h": fopen, fclose, fread, fwrite, yet not
fsize because that is often a platform-dependent function, so to get
the file size in standard C you open the file, use fseek to seek to
the end, and get the offset with ftell, that's the same as fsize, but
some compiler facility import libraries have. Then, in C++, instead
of "stdio.h", it's <cstdio>, to then use the ::fopen functions in C+
+. They left the C standard import libraries for standard libraries
in C++, some of them.

Of course, in the platform libraries, for using files, sometimes it's
better to use memory mapping with the virtual memory machinery more
directly, "mmap.h" or on Windows OpenFileForViewMapping or whatever.
Also for example looking through the compiler's include directory
inc/, platforms have vectorization intrinsic support, "dvec.h".

Working through directories is called "file tree walk".

http://linux.die.net/man/3/ftw

To implement directory traversal in Windows, there is FindFirstFile/
FindNextFile with the struct FILEINFO so it would be good, in C++, to
implement a class to encapsulate that regular behavior as an iterator
over the directory contents by file type and offset. (Includes ., ..,
Windows FindtFirstFile and FindNextFile's results are the directory
links.)

class directory
{
directory(string)
iterator& begin()
iterator& end()
}

There is time involved with that for generating the iterator adaptors
to use the system APIs to get the directories under a directory. That
is where, using the system APIs to list the directory contents returns
iterators over various kinds of objects: file, directories, links,
any file system directory API object (handle), the system can make the
list whatever it wants.

Then just have something like

for_each_file(string pattern)

to implement that on Linux and Windows, it's separate for each,
because something like the command "ls" on Linux returns the variety
of output of the standard systems, also Windows has an advanced file
system with the shell namespace.


http://www.ddj.com/windows/184416631

That is an article discussing adapting Windows enumerator APIs to STL
iterator APIs.

Ross F.
 
J

James Kanze

Because in Linux or Unix the directories are actually files.

That was more or less true in early Unix (but you couldn't open
them for writting). But it's certainly not true in Linux or
Unix today---like most systems, Linux and Unix support different
file systems, and whether you can open a directory like you open
a file depends on the file system, not the OS. Under most Unix
today, for example, this will work for a local file, but it
fails for an NFS mounted file (and probably for an SMB mounted
file as well, but I've never tried it). Basically, it's not
reliable anywhere.
On Windows there's a DOS file attribute or so, the file system
attribute.

Yes, but at least for the old DOS file systems, directories were
as much files as they were under early Unix. It is simply a
choice of the system authors that the standard open call would
fail if the "file" had a directory type.
So you can't open directories with fopen on Windows. On
Windows, getting the directory is null, because as a file it's
null. (I could be wrong, also it could be set up to return an
open file handle if you use your own API routines around the
Windows API.) On Unix, getting the directory is a file, with
a list of the files in the directory.

If you successfully open a local directory, you still don't have
just a list of files. You have some sort of structured
data---it was definitely different between version 7 and Berkley
4.2, for example.
On some Unix machines the directories have user and group
attributes so the directory access could be made null on Unix
also.

On all Unix machines, directories (and anything else that can be
found in the file system---named pipes, etc.) always have user,
group and global access rights. Whether they really mean
anything for remotely mounted files is harder to say---with NFS
running on a Unix based server, they certainly do, but I'd be
less sure for SMB running on a Windows machine. (SMB, on the
other hand, supports the Windows access control scheme, which is
a lot more sophisticated than what Unix provides.)
In C++ it's called ::fopen(const char*, ...);, let's see, the
second argument to fopen is the collected open flags so it
could be a byte, probably it is a default numeric type the
integer: int.

Whether it's called ::fopen or std::fopen depends on whether you
included <stdio.h> or <cstdio>. And the second argument is a
string (C style, of course, since it's a function inherited from
the C library).
fopen(const char*, const char*);
Why it's ::fopen instead of fopen() is that then other C++
programs in the same code could use fopen instead of ::fopen.
In C++ it's standard that ::fopen() is the same as fopen().

No. In C++ ::fopen is the function declared in the global
namespace (in <stdio.h>), std::fopen is the one declared in std
(in <cstdio>), and fopen, without any qualification, is
whichever one the compiler happens to find (and thus might be a
user defined function, totally unrelated to the standard fopen).
 
R

Ross A. Finlayson

That was more or less true in early Unix (but you couldn't open
them for writting).  But it's certainly not true in Linux or
Unix today---like most systems, Linux and Unix support different
file systems, and whether you can open a directory like you open
a file depends on the file system, not the OS.  Under most Unix
today, for example, this will work for a local file, but it
fails for an NFS mounted file (and probably for an SMB mounted
file as well, but I've never tried it).  Basically, it's not
reliable anywhere.


Yes, but at least for the old DOS file systems, directories were
as much files as they were under early Unix.  It is simply a
choice of the system authors that the standard open call would
fail if the "file" had a directory type.


If you successfully open a local directory, you still don't have
just a list of files.  You have some sort of structured
data---it was definitely different between version 7 and Berkley
4.2, for example.


On all Unix machines, directories (and anything else that can be
found in the file system---named pipes, etc.) always have user,
group and global access rights.  Whether they really mean
anything for remotely mounted files is harder to say---with NFS
running on a Unix based server, they certainly do, but I'd be
less sure for SMB running on a Windows machine. (SMB, on the
other hand, supports the Windows access control scheme, which is
a lot more sophisticated than what Unix provides.)


Whether it's called ::fopen or std::fopen depends on whether you
included <stdio.h> or <cstdio>.  And the second argument is a
string (C style, of course, since it's a function inherited from
the C library).


No.  In C++ ::fopen is the function declared in the global
namespace (in <stdio.h>), std::fopen is the one declared in std
(in <cstdio>), and fopen, without any qualification, is
whichever one the compiler happens to find (and thus might be a
user defined function, totally unrelated to the standard fopen).

Yes, the file systems and how then fopen() opens file from the file
namespace definitely influence the behavior of directories as files.

On Windows NT the file system is often NTFS, or FAT32, with the file
"reparse" points. On Unix it is often a Unix file system with the
symbolic links, and then as you mention for example NFS, Network File
System, or SMB, that Windows uses for file sharing, or for example zip
files when the shell extension is how the users are used to
interacting with the files, different volumes. In the standard
libraries, there are the opendir, readdir, writedir for Unix among the
other functions whose names end with dir, and FindFirstFile for
Windows. The struct stat is in the standard C API, that is some file
statistics. With the example of lsof or filemon, outside standard C++
there would be expected replete function on Windows or Unix.

Besides data files, devices can be read and written using their
names. Named pipes, sockets, messages, share objects, lots of things
named in hierarchical structure often with links in the file system.

Here, an idea of maintaining a working directory accumulator in the
function prolog can help when the function must be general purpose but
it's a coincidence that all the inputs share the same directory, for
example.

Yes, you're quite right, there was just one thing or two that I would
argue. No, actually there's none. I just thought stdio.h would
automatically bring in fopen, so I'm wondering about implementations
that allow it, besides not having namespace scoping in C, so it would
be around __cplusplus.

Thank you!

Ross F.
 
J

James Kanze

On Oct 11, 4:05 am, James Kanze <[email protected]> wrote:
Yes, the file systems and how then fopen() opens file from the
file namespace definitely influence the behavior of
directories as files.
On Windows NT the file system is often NTFS, or FAT32, with
the file "reparse" points. On Unix it is often a Unix file
system with the symbolic links, and then as you mention for
example NFS, Network File System, or SMB, that Windows uses
for file sharing, or for example zip files when the shell
extension is how the users are used to interacting with the
files, different volumes.

This depends very much on context. On my Linux machine at home,
yes, there are four mounted file systems, two are ext3 and two
are NTFS. At work, most of the file systems have been NFS on
the Unix systems, SMB on the Windows systems. Of these, ext3
(and NTFS) allow opening the directory as a file, and then only
under Linux. Neither NFS nor SMB allow it, and the Windows OS
doesn't allow it.

[...]
I just thought stdio.h would automatically bring in fopen, so
I'm wondering about implementations that allow it, besides not
having namespace scoping in C, so it would be around
__cplusplus.

According to the standard, <stdio.h> brings in ::fopen, and
maybe std::fopen (both are, of course, the same function).
<cstdio> only brings in std::fopen. In practice (and in the
next version of the standard), <cstdio> may also bring in
::fopen. And typically, the fopen which is introduced will be
``extern "C"'', which also have implications. Never the less,
given something like:

#include <stdio.h>

struct Toto
{
void* fopen( char const*, char const* ) ;
void f() ;
}

void
Toto::f()
{
fopen( "abc", "def" ) ; // calls Toto::fopen
::fopen( "abc", "def" ) ; // calls the standard fopen
std::fopen( "abc", "def" ) ;// either calls the
// standard fopen, or fails
// to compile
}

A user can also define new fopen's in his own namespaces, which
can cause surprises as well. (Not that I think this is a good
idea.)
 
R

robertwessel2

Yes, but at least for the old DOS file systems, directories were
as much files as they were under early Unix.  It is simply a
choice of the system authors that the standard open call would
fail if the "file" had a directory type.


Well, directories on FAT are not quite complete files. For example
the root directory on FAT12 or FAT16 volumes is not a file-like-object
at all (on FAT32 it is), and in all cases directories do not have
valid length* fields in their defining directory entries (ignoring the
root directories on FAT12/16, which don't have those). So the
semantics of opening such a "file" would be questionable.


*The length of a (non-FAT12/16 root) directory structure is implicit
in the chain of clusters allocated to it.
 
J

James Kanze

Well, directories on FAT are not quite complete files. For example
the root directory on FAT12 or FAT16 volumes is not a file-like-object
at all (on FAT32 it is), and in all cases directories do not have
valid length* fields in their defining directory entries (ignoring the
root directories on FAT12/16, which don't have those). So the
semantics of opening such a "file" would be questionable.

Yes. More generally, root directories will often follow
different rules than other directories. (And of course, some
systems only have a root directory, no hierarchies allowed.) As
soon as a directory "lives" in another directory, and you can
"navigate" to it, however, it needs to be somewhat "filelike".
(All of which is, of course, very vague. Each system, and each
filesystem on the system, has its own set of rules.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top