how to check if a command line argument ends with .txt

I am trying to write a program which checks if one of the command line argument entered ends with .txt . If one of the arguments ends with .txt, then an input file to read is supplied and then I will generate an output file called example.txt.

The problem with my code occurs at the line:

size_t pos = argvj.find(".txt");

If the program cannot find the .txt extension for argvj , then pos will return a very huge number which is not what i want and on the next line i will get substring out of range.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 
int main(int argc, char *argv[])
{

	for (int i = 1; i < argc; i++)
	{

		if (argc >= 3 && !strcmp(argv[i], "-r"))

		{

			//cout << countLines(argv[2]);


			for (int j = 1; j < argc; j++)
			{
				string argvj(argv[j]);

				size_t pos = argvj.find(".txt");

				string subj = argvj.substr(pos);


				if (subj == ".txt")

				{
					ofstream myfile;
					myfile.open("example.txt");
					myfile << "Writing this to a file.\n";
					myfile << "Writing this file.\n";

					myfile.close();

				}

			}

		}

	}

}


Line 19: You never check that the find was successful. if string::find fails, it returns string::npos. You need to check if npos was returned.
http://www.cplusplus.com/reference/string/string/find/

1) watch for case ... .TXT vs .txt are not uncommon.
2) all this conversion to strings for the sake of converting to strings for the sake of avoiding a C-string function does not sit well with me. Lookit strstr(..) function. If its null, its not there, if its a valid pointer, it was. You should be able to fix the find command but I know that strstr works here and is very simple to use.
3) this is very limiting. Many people have extensionless text files, or oddball extensions for files that really are text files. You can do it, but are you shooting your user in the foot?

watch for case ... .TXT vs .txt are not uncommon.

std::regex might help once you've converted the command line arguments as discussed on the other thread:
1
2
3
4
5
6
7
8
9
10
11
# include <iostream>
# include <regex>
# include <iomanip>

int main()
{
    std::string fileName = "C:\\test.tXT";
    std::regex re (".*\\.txt$", std::regex::icase);
    std::cout << std::boolalpha <<  std::regex_match(fileName, re);
}
//introduction to C++ regex: http://www.cplusplus.com/reference/regex/ECMAScript/ 
Hello masterinex,

If you want to try something simple this might work:

1
2
3
std::string temp{ argv[1] };
size_t pos = temp.find(".txt", 0);
if (pos == std::string::npos) temp += ".txt";


This only finds the "." and not all four characters, but it works. Or you could remove the "txt" in line 2 and it still works. The "1" in line 1 can be replaced with "j" to work in your for loop.

Hint: an example of what you might put on the command line as arguments would help to see what you are doing.

Hope that helps,

Andy
all that code to avoid
if(strstr(argv[1], ".txt") )
//its there! however it will also trip over -rstuff ... up to you if this matters ... most of the other examples here can be "fooled" with crazed input as well.




Last edited on
Hello masterinex.

I ask again what fo your command line arguments look like? is there a certain order to these arguments or could they be in any order? Do you expect the file name to have a ".txt" extension or could it be added by the program?

The if statement at line 24 might work better with an "else" to let the user know the correct way of entering the file name.

Until I know more I am working with "argv[1]" as being the file name and that may not be what you are actually doing.

Andy
The code will check for ".txt" in any command line argument, so long as "-r" is one of args. So it will trigger on:
myProg -r foo.txt
myProg -x foo.txt -r burp x y z
myProg squiggly.txt -r larry.txt curly.txt moe.txt

It also will trigger on .txt anywhere in the argument:
myProg -r this.is.not.txt.pdf


The code below will find any argument ending in .txt (case insensitive) if one of the arguments is "-r". Since it isn't clear which arg should be the txt file, I left that logic intact.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string>
#include <cctype>
#include <cstring>

using namespace std;

void
stringToLower(string &str)
{
    for (size_t i=0; i<str.size(); ++i) {
	if (isalpha(str[i])) {
	    str[i] = tolower(str[i]);
	}
    }
}

int
main(int argc, char **argv)
{
    for (int i = 1; i < argc; i++) {
	if (argc >= 3 && strcmp(argv[i], "-r") == 0) {
	    //cout << countLines(argv[2]);
	    for (int j = 1; j < argc; j++) {
		string argvj(argv[j]);
		if (argvj.size() >= 4) {
		    stringToLower(argvj);
		    if (argvj.find(".txt", argvj.size()-4) != string::npos) {
			cout << "found " << argv[j] << '\n';
		    }
		}
	    }
	}
    }
    return 0;
}

Topic archived. No new replies allowed.