I write an application, which get a number from args.
ex:./app --addr 0x123 or ./app --addr 123
I don't know which api can identify if the parameter is hex or decimal and come up with the correct data?
Thanks.
One way of getting the arguments is through argc and argv in
int main(int argc, char* argv[]) {
// Some code here.
return;
}
This will give you an array with all the arguments as strings. For example:
#include <iostream>
int main(int argc, char* argv[]) {
for (int i{ 0 }; i < argc; i++)
std::cout << "Argument " << i << ": " << argv[i] << std::endl;
return;
}
will give you this output
$ ./app --addr 0x123
Argument 0: ./app
Argument 1: --addr
Argument 2: 0x123
Now, parsing the input is a bit more complicated. You could create your own logic to evaluate the arguments once you have the arguments as strings.
You could also use libraries. For example, you could use argparse by p-ranav. GitHub repository here. It is an easy to use library so you don't have to worry about positioning of the arguments.
Answering you question, how do you know if the input is hex or decimal? You could check if the argument after --addr starts with 0x or not. That could be one way, but you have to also consider the case when someone inputs a hex value without the 0x, like ./app --addr A2C. Also, documenting acceptable formats and printing a --help message can ameliorate this.
You have to think about your specific application, but some steps once you have the string after --addr that you could do to check input is:
Does it start with 0x?
If not, does it contains characters 0-1 and A-F? Is this allowed?
Is it in a valid range?
For example, 0xFFFFF is 1,048,575. But you may only want addresses up to 0xFFFF (65,535). What should happen in these cases?
Hope it helps!
P.S.: These are all implementations in C++.
strtol will do the job for you
https://www.cplusplus.com/reference/cstdlib/strtol/
Related
I have a task which is ,I need to display to the user the mathematical tables the user wants . However the input must be using command line arguments . I know how to do this with single input .. however I have to add a functionality so that when the user types : my program 5-7 i have to display the multiplicative tables of 5 ,6 and 7 ..how can I work around with this ?
This must be done in C++
This is my code :
#include <iostream>
#include <cctype>
#include <iomanip>
using namespace std;
int main(int argc,char *argv[]){
int i;
locale loc;
int tables[argc-1];
if(argc <=1){
cout<<"NO ARGUMENTS PASSED"<<endl;
exit(0);
}
for(i=1;i<=argc;i++){
if( isdigit(argv[i],loc)){
tables[i] = atoi(argv[i]);
}
}
for (i=1; i<=argc;i++){
cout<<argv[i]<<endl;
}
}
First decide whether to handle "tables 2-3", "tables 2 - 3" or both.
Lets say we opt for both. So argc must be either 2 for the one argument case (remember argv[0] is the program name) or 4 for the three argument case. if it's not 2 or 4, print out a usage message to tell the user how to use the program and quit.
If is is 4, argv[1] must be an integer, argv[2] must be the string "-", and argv[3] must be an integer. You might also require that argv[3] be greater than argv[1].
So test these. strcmp will test for the middle. The function strtol() provides an easy way to test for a valid integer (the end pointer should point to the nul on exit if it is an integer).
If argc is 2, the string must be in the forma 12-30, an integer,a hyphen and an integer. There are several ways of testing for this. One is to call strtol, then check that the end pointer points to hyphen, then call strtol again on the character after the hyphen. You could also use the function sscanf().
Now decide how to handle cases such as negative or zero, and huge values maybe beyond your machine precision.
Finally print out the tables.
This has been driving my entire C++ class nuts, none of us has been able to find a solid solution to this problem.
We are passing information to our program through the Terminal, via argv* [1]. We would call our program ./main 3 and the program will run 3 times.
The problem comes when we are validating the input, we are trying to cover all of our bases and for most of them we are good, like an alphabetical character entered, a negative number, 0, etc. But what keeps passing through is an int followed by a str for example ./main 3e or ./main 1.3. I've tried this
( Ashwin's answer caught my eye ) but it doesn't seem to work or at least I can't implement it in my code.
This is my code now:
int main(int argc, char * argv[]){
if (!argv[1]) exit(0);
int x = atoi(argv[1]);
if (!x or x <= 0) exit(0);
// I would like to add another exit(0); for when the input mixes numbers and letters or doubles.
for (int i = 0; i < x; i++){
// rest of the main func.
}
Despite the title, it sounds like you really want to do is check whether every single character in the input argument is a digit. You can achieve this by iterating over it, checking that every element is a digit using std::isdigit.
Here's a sketch using the std::all_of algorithm:
size_t len = strlen(argv[1]);
bool ok = std::all_of(argv[1], argv[1] + len,
[](unsigned char c) { return std::isdigit(c); } );
You can add an extra check for the first element being '0' if needed.
If you want to convert a string to a number and verify that the entire string was numeric, you can use strtol instead of atoi. As an additional bonus, strtol correctly checks for overflow and gives you the option of specifying whether or not you want hexadecimal/octal conversions.
Here's a simple implementation, with all the errors noted (printing error messages from a function like this is not a good idea; I just did it for compactness). A better option might be to return an error enum instead of the bool, but this function returns a std::pair<bool, int>: either (false, <undefined>) or (true, value):
std::pair<bool, int> safe_get_int(const char* s) {
char* endptr;
bool ok = false;
errno = 0; /* So we can check ERANGE later */
long val = strtol(s, &endptr, 10); /* Don't allow hex or octal. */
if (endptr == s) /* Includes the case where s is just whitespace */
std::cout << "You must specify some value." << '\n';
if (*endptr != '\0')
std::cout << "Argument must be an integer: " << s << '\n';
else if (val < 0)
std::cout << "Argument must not be negative: " << s << '\n';
else if (errno == ERANGE || val > std::numeric_limits<int>:max())
std::cout << "Argument is too large: " << s << '\n';
else
ok = true;
return std::make_pair(ok, ok ? int(val) : 0);
}
In general, philosophical terms, when you have an API like strtol (or, for that matter, fopen) which will check for errors and deny the request if an error occurs, it is better programming style to "try and then check the error return", than "attempt to predict an error and only try if it looks ok". The second strategy, "check before use", is plagued with bugs, including security vulnerabilities (not in this case, of course, but see TOCTOU for a discussion). It also doesn't really help you, because you will have to check for error returns anyway, in case your predictor was insufficiently precise.
Of course, you need to be confident that the API in question does not have undefined behaviour on bad input, so read the official documentation. In this case, atoi does have UB on bad input, but strtol does not. (atoi: "If the value cannot be represented, the behavior is undefined."; contrast with strtol)
I can see there are many questions related to strings and wide strings. But as none of them gives me information I am looking for... I am posting a new question.
I have this code...
std::string myName("vikrant");
std::cout<<myName<<std::endl;
std::wstring myNameHindi = L"मुरुगन";
std::wcout<<myNameHindi<<"-----"<<myNameHindi.size()<<std::endl;
std::wcout<<L"मुरुगन"<<std::endl;
std::string myNameHindiS = "मुरुगन";
std::cout<<myNameHindiS<<"-----"<<myNameHindiS.size()<<std::endl;
when I compile & run this code on my RHEL box(... (connected through ssh, running gcc 4.1.2) I get this o/p (please note middle two lines are not printing properly)
vikrant
.A0A(-----6
.A0A(
मुरुगन-----18
While on my apple laptop and one of FreeBSD(through ssh) box I dont get o/p from w_* code. I just get first and last cout executed
vikrant
मुरुगन-----18
My understanding was that if not specified these strings will be treated as UTF 8. and if string can handle it wstring will handle as well. Is there something wrong in that approach?
Some addon questions are...
is it just a display problem? or wstring is not reliable on linux?
Any additional information may help as well.
EASIEST WAY
Here is what are you looking for, #include <clocale> and for example, to have Turkish, just simply type setlocale(LC_ALL,"Turkish"); to your code.
You can also just leave it as setlocale(LC_ALL,""); it will use your local language.
#include <iostream>
#include <clocale>
int main(){
setlocale(LC_ALL,"Turkish");
std::cout << "I can type any Turkish character like ÖöÇ窺İiĞğÜüİ, anything.\n" << std::endl;
system("pause");
return 0;
}
SOME OTHER WEIRD WAY TO DO IT
This is a really weird way to do it but it will also work.
#include <iostream>
int main()
{
std::string characters="IiĞğÇçÜüŞşÖö";
int i;
for ( i=0; i<characters.length(); ++i ){
characters[i]=(characters[i]==-2) ? 159:characters[i]; //ş
characters[i]=(characters[i]==-3) ? 141:characters[i]; //ı
characters[i]=(characters[i]==-4) ? 129:characters[i]; //ü
characters[i]=(characters[i]==-10) ? 148:characters[i]; //ö
characters[i]=(characters[i]==-16) ? 167:characters[i]; //ğ
characters[i]=(characters[i]==-25) ? 135:characters[i]; //ç
characters[i]=(characters[i]==-34) ? 158:characters[i]; //Ş
characters[i]=(characters[i]==-35) ? 152:characters[i]; //İ
characters[i]=(characters[i]==-36) ? 154:characters[i]; //Ü
characters[i]=(characters[i]==-42) ? 153:characters[i]; //Ö
characters[i]=(characters[i]==-48) ? 166:characters[i]; //Ğ
characters[i]=(characters[i]==-57) ? 128:characters[i]; //Ç
std::cout << characters[i] << " ";
}
}
Here is were i get the error.
To explain, i want to print the → character which according to http://www.endmemo.com/unicode/unicodeconverter.php
The code is 2192. but i may be using the wrong code if so what is the right way to print → .
int _tmain(int argc, _TCHAR* argv[])
{
UINT oldcp = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8);
cout<<"\x2192"<<endl;
SetConsoleOutputCP(oldcp);
return 0;
}
A char on your platform is 8 bits. Your code part "\x2192" tries to put 16 bits in it. What will not fit, so you get the warning.
You possibly meant several characters, like "\x21\x92" or "\x92\x21"? That creates a valid string with two chars (+ the 0). You may still adjust it to have the proper value if comments are correct.
From the use of _tmain and SetConsoleOutputCP I guess you are mostly about Windows. I'm afraid I don't know much about that; hopefully someone who knows more about that specific case will chime in, but this program generates the output you're looking for in a quick test I tried here with a UTF-8 terminal. Here's the program:
#include <iostream>
int main(void)
{
std::cout << "\xE2\x86\x92" << std::endl;
return 0;
}
And example output:
$ make example && ./example
c++ example.cpp -o example
→
I just directly output the UTF-8 encoding of the → character.
Equivalently (at least for clang):
#include <iostream>
int main(void)
{
std::cout << "→" << std::endl;
return 0;
}
How print format string passed as argument ?
example.cpp:
#include <iostream>
int main(int ac, char* av[])
{
printf(av[1],"anything");
return 0;
}
try:
example.exe "print this\non newline"
output is:
print this\non newline
instead I want:
print this
on newline
No, do not do that! That is a very severe vulnerability. You should never accept format strings as input. If you would like to print a newline whenever you see a "\n", a better approach would be:
#include <iostream>
#include <cstdlib>
int main(int argc, char* argv[])
{
if ( argc != 2 ){
std::cerr << "Exactly one parameter required!" << std::endl;
return 1;
}
int idx = 0;
const char* str = argv[1];
while ( str[idx] != '\0' ){
if ( (str[idx]=='\\') && (str[idx+1]=='n') ){
std::cout << std::endl;
idx+=2;
}else{
std::cout << str[idx];
idx++;
}
}
return 0;
}
Or, if you are including the Boost C++ Libraries in your project, you can use the boost::replace_all function to replace instances of "\\n" with "\n", as suggested by Pukku.
At least if I understand correctly, you question is really about converting the "\n" escape sequence into a new-line character. That happens at compile time, so if (for example) you enter the "\n" on the command line, it gets printed out as "\n" instead of being converted to a new-line character.
I wrote some code years ago to convert escape sequences when you want it done. Please don't pass it as the first argument to printf though. If you want to print a string entered by the user, use fputs, or the "%s" conversion format:
int main(int argc, char **argv) {
if (argc > 1)
printf("%s", translate(argv[1]));
return 0;
}
You can't do that because \n and the like are parsed by the C compiler. In the generated code, the actual numerical value is written.
What this means is that your input string will have to actually contain the character value 13 (or 10 or both) to be considered a new line because the C functions do not know how to handle these special characters since the C compiler does it for them.
Alternatively you can just replace every instance of \\n with \n in your string before sending it to printf.
passing user arguments directly to printf causes a exploit called "String format attack"
See Wikipedia and Much more details
There's no way to automatically have the string contain a newline. You'll have to do some kind of string replace on your own before you use the parameter.
It is only the compiler that converts \n etc to the actual ASCII character when it finds that sequence in a string.
If you want to do it for a string that you get from somewhere, you need to manipulate the string directly and replace the string "\n" with a CR/LF etc. etc.
If you do that, don't forget that "\\" becomes '\' too.
Please never ever use char* buffers in C++, there is a nice std::string class that's safer and more elegant.
I know the answer but is this thread is active ?
btw
you can try
example.exe "print this$(echo -e "\n ")on newline".
I tried and executed
Regards,
Shahid nx