How to open a file ending with a particular extension in C++ - c++

I am trying to write a lexer using flex, and want to open and read from a file ending with a particular extension. E.g filename.k. I am only able to do it if I specify the file name as well as the extension.
FILE *myfile = fopen("a.k", "r");
if (!myfile) {
cout << "I can't open a.k!" << endl;
Can someone show me the way to open *.k files in C++.
I am running flex on Ubuntu. What I am trying to do is to run a flex program. The above code executes fine. I wanted a way where I can open a file with .k extension irrespective of the file name. Example. ./myprogram a.k or ./myprogram b.k. In the above example I always have to specify the file name in the code itself all the time.

Comment to Basile's anser:
[...] Such as ./myprogram a.k, I wanted a way where I can write any filename instead of a but ending with a .k extension.
While the cited answer technically is correct, I think your true problem is how to get some arbitrary, but specific file path from the command line:
Example: ./myprogram a.k or ./myprogram b.k
The thing is quite easy: you get the command line parameters passed directly to your main function, provided you use the variant accepting them:
int main(int argc, char* argv[]);
First parameter (argv[0]) is always the name of your programme (or an empty string, if not available), so argc will always be at least one. Afterwards the parameters provided follow, so invoking "./myprogram b.k" will result in argc being two and argv pointing to a char* array equivalent to the following:
char* argv[] =
{
"./myprogram",
"b.k",
nullptr // oh, yes, the array is always null terminated...
};
And then, the matter gets easy: Check, if the parameter is given at all: if(argc == 2) or, if you are willing to accept but ignore any additional parameters, if(argc >= 2) or simply if(argv[1]) (as it will be nullptr, if no parameter given, or the first parameter otherwise) and then use it for fopen or, if you prefer a more C++ like way, to open a std::ifstream. You might want to have additional checks, e. g. if the file name really ends with ".k", but that's up to you now...

Your fopen-ing code is good, but running in conditions (e.g. in some weird working directory, or without sufficient permissions) which make the fopen fail.
I recommend to use errno (perhaps implicitly thru perror) in that failure case to get an idea of the failure reason:
FILE *myfile = fopen("a.k", "r");
if (!myfile) {
perror("fopen of a.k");
exit(EXIT_FAILURE);
}
See e.g. fopen(3), perror(3), errno(3) (or their documentation for your particular implementation and system).
Notice that file extensions don't really exist in standard C++11 (but C++17 has filesystem). On Linux and POSIX systems, file extensions are just a convention.
Can someone show me the way to open *.k files in C++.
If you need to open all files with a .k extension, you may rely on globbing (on POSIX, run something like yourprog *.k in your shell, which will expand the *.k into a sequence of file names ending with .k before running your program, whose main would get an array of arguments; see glob(7)), or you have to loop explicitly using operating system primitives or functions (perhaps with glob(3), nftw(3), opendir(3), readdir(3), ... on Linux; for Windows, read about FindFirstFile etc...)
Standard C++11 don't provide a way to iterate on all files matching a given pattern. Some framework libraries (Boost, Poco, Qt) do provide such a way. Or you need to use operating system specific functions (e.g. to read the current directory. But directories are not known to C++11 and are an abstraction provided by your operating system). But C++17 has filesystem, but you need a very recent compiler and C++ standard library to get that.
BTW, on Unix or POSIX systems, you could have one single file named *.k. Of course that is very poor taste and should be avoided (but you might run touch '*.k' in your shell to make such a file).
Regarding your edit, for Linux, I recommend running
./myprogram *.k
(then your shell will expand *.k into one or several arguments to myprogram)
and code the main of your program myprog appropriately to iterate on arguments. See this.
If you want to run just myprogram without any additional arguments, you need to code the globbing or the expansion inside it. See glob(3), wordexp(3). Or scan directories (with opendir(3), readdir(3), closedir, stat(2) or nftw(3))

Related

PATH in argv when running debugger Visual Studio

I am running a C++ program with argc and argv, when i run from the command line it passes in 12 args, the name of the exe and 11 other arguments. i pass the same arguemnt list in visual studio for example: ./euchre.exe pack.in shuffle 10 Alice Simple Bob Simple Cathy Simple Drew Simple
However when passing the same thing in the argument list in Visual Studio debugger, it also includes the file path as an argv at [0], making the total count 13. My program does not assume 13 arguments, so is there a way i can prevent VS from using the PATH as an argument?
EDIT: argv is the executable name with the file PATH Attached to the executable name : path/to/file/euchre.exe need to make sure that the PATH is not attached to the exe instead: ./euchre.exe, not sure if there is a way to do that
According to the C++ standard, (if arc > 0) argv[0] is either an empty string or it represents the name used to invoke the program. The exact representation is unspecified.
According to POSIX standard, the value in argv[0] should point to a filename string that is associated with the process being started by one of the exec functions.
Note that neither standard guarantee that the path is a relative one, like you would want it to be.
Given that command that argv[0] is some representation of the program name, if you simply pass 11 arguments to the program, then there will be argv[0]...argv[11] i.e. 12 total arguments in main. If you pass 12 arguments to the program, then there will be argv[0]...argv[12] i.e. 13 total arguments in main
Microsoft documentation says that argv[0] is by convention the command with which the program is invoked. Assuming this convention, you can achieve a relative path by using a relative path to execute the program. I don't know if it is possible to configure visual studio debugger to execute the program with a relative path to the working directory.
However, it would instead be preferable to not let your program depend on whether the program was run with a relative path (such as ./filename.exe), or using an absolute path. It is a bad design to rely on one or the other. For example, if you need the name of the executable without the directory that contains it, you can use std::filesystem::path::filename on argv[0]. This works whether the path is absolute or relative.
In the unconventional case where argv[0] is not a file name at all, the above won't work. There is no standard way in C++ to get the file name of the running process. You would need to rely on a OS specific way to get it.
Strictly speaking the value of argv[0] is implementation defined, see this answer for a good explanation.
So you need to go to the Microsoft docs to to find out how this is implemented for Visual Studio. To quote the relevant section:
Note
By convention, argv[0] is the command with which the program is
invoked. However, it is possible to spawn a process using
CreateProcess and if you use both the first and second arguments
(lpApplicationName and lpCommandLine), argv[0] may not be the
executable name; use GetModuleFileName to retrieve the executable
name, and its fully-qualified path.
Basically, the value of argv[0] will depend on how the program is invoked, and you can't really control how the debugger will invoke your program.

Getting the string(s) output from an exec command in C++

My problem is pretty simple, but I can't seem to find anything straightforward or specific to what I am trying to do. I'm simply using execl to list the files in the current folder that follow the same pattern (ie, execl("ls nameOfFile*.txt")). What I want to do now is grab those file names so that I can loop through and get the data out of them. Is there a simple way of doing this? Am I using the correct exec?
Thanks for any help or tips.
The signature of execl is
int execl(const char *path, const char *arg, ...);
You're supposed to pass the path to the executable as the first argument, and arguments for the executable as the subsequent arguments, so your calling syntax is wrong. Even if you fix that, it still won't do what you want. The only way execl and friends ever return control to the calling program is if an error occurs. This answer contains an excellent explanation of what execl does.
You were probably thinking of std::system, which you can pass an arbitrary string to, and have the OS execute that command. While that'll print the filenames to stdout, it's still not what you want, because system returns an error code resulting from executing the command line you specified, it has no way of capturing and returning whatever may be written to stdout by the command.
Unfortunately, there is nothing in the C++ standard library (yet) that allows you to list and iterate files from the filesystem. The preferred cross platform approach is to use Boost.Filesystem. Otherwise, there are platform specific APIs available, which are listed in this answer, along with a Boost usage example.

How to move files in Qt?

Is there a cross-platform function in Qt that is equivalent to the MoveFile function in Windows and the mv command in Linux?
Sure, QDir::rename() following the old Unix / POSIX tradition of calling this rename.
Which makes sense if you think of a file with its complete path: the underlying inodes just get assigned a different path/file label.
You would use QDir::rename() but be beware of the special cases when rename() can fail:
On most file systems, rename() fails
only if oldName does not exist, if
newName and oldName are not on the
same partition or if a file with the
new name already exists. However,
there are also other reasons why
rename() can fail. For example, on at
least one file system rename() fails
if newName points to an open file.
QUrlOperator::copy() is an alternative to QDir::rename() that may also work for you.

C++: How to check if a file/directory is readable? (PHP equivalent: is_readable)

I am trying to validate a directory with C++.
http://php.net/manual/en/function.is-readable.php
bool is_readable ( string $filename )
Tells whether a file (or directroy) exists and is readable.
What would be the equivalent of the above in C++?
I am already using the boost/filesystem library to check that the directory exists.
I have checked the documentation:
http://www.boost.org/doc/libs/1_44_0/libs/filesystem/v3/doc/index.htm
but I cannot find the equivalent of PHP's is_readable().
If it is not possible with the boost/filesystem library, what method would you use?
Since you've tagged the question "Linux", there is a POSIX function to check if the file is readable/writable/executable by the user of the current process. See man 2 access.
int access(const char *pathname, int mode);
For example,
if (-1 == access("/file", R_OK))
{
perror("/file is not readable");
}
Alternatively, if you need portability, try to actually open the file for reading (e.g. std::ifstream). If it succeeds, the file is readable. Likewise, for directories, use boost::filesystem::directory_iterator, if it succeeds, directory is readable.
Most operating systems provide stat().

How do you determine full paths from filename command line arguments in a c++ program?

I am writing a program in c++ that accepts a filename as an argument on the command line:
>> ./myprogram ../path/to/file.txt
I know I can simply open an fstream using argv[1], but the program needs more information about the exact location (ie. full pathname) of the file.
I thought about appending argv[1] to getcwd(), however obviously in the example above you'd end up with /path/../path/to/file.txt. Not sure whether fstream would resolve that path automatically, but even if it did, I still don't have the full path without a lot of string processing.
Of course, that method wouldn't work at all if the path provided was already absolute. And since this program may be run on Linux/Windows/etc, simply detecting a starting '/' character won't work to determine whether the argument was a full path or not.
I would think this is a fairly common issue to deal with path names across multiple OSs. So how does one retreive the full path name of a command line argument and how is this handled between operating systems?
Pathname handling is highly OS-specific: some OS have a hierarchy with just one root (e.g. / on Unix ), some have several roots a la MS-DOS' drive letters; some may have symbolic links, hard links or other kinds of links, which can make traversal tricky. Some may not even have the concept of a "canonical" path to a file (e.g. if a file has hard links, it has multiple names, none of which is more "canonical").
If you've ever tried to do path-name manipulation across multiple OS in Java, you know what I mean :-).
In short, pathname handling is system-specific, so you'll have to do it separately for each OS (family), or use a suitable library.
Edit:
You could look at Apache Portable Runtime, or at Boost (C++ though), both have pathname handling functions.
...you'd end up with /path/../path/to/file.txt. Not sure
whether fstream would resolve that
path automatically, but even if it
did, I still don't have the full path
without a lot of string processing.
It does, and you can use /path/../path/ for everything you want without problems.
Anyway there is no standard function in C++ to do what you want. You would have to do it manually, and it wouldn't be trivial.. I suggest you keep the path as it is, it shouldn't cause any problems.
It is OS-dependent. If you are using linux you can look at realpath(). No doubt Windows has something comparable.
AFAIK there is no standard way.
however you could try this approach (written in pseudocode):
string raw_dirname=get_directory_part(argv[1])
string basename=get_filename_part(argv[1])
string cwd=getcwd()
chdir(relative_dirname)
string absolute_dirname=getcwd()
chdir(cwd)
string absolute_filename=absulute_dirname + separator + basename
but note: I am not quite sure if there are issues when symbolic links come into play.