Properly handle escape sequences in strings from argv in C++ - c++

I'm writing a larger program that takes arguments from the command line after the executable. Some of the arguments are expected to be passed after the equals sign of an option. For instance, the output to the log is a comma separated vector by default, but if the user wants to change the separator to a period or something else instead of a comma, they might give the argument as:
./main --separator="."
This works fine, but if a user wants the delimiter be a special character (for example: tab), they might expect to pass the escape sequence in one of the following ways:
./main --separator="\t"
./main --separator='\t'
./main --separator=\t
It doesn't behave the way I want it to (to interpret \t as a tab) and instead prints out the string as written (sans quotes, and with no quotes it just prints 't'). I've tried using double slashes, but I think I might just be approaching this incorrectly and I'm not sure how to even ask the question properly (I tried searching).
I've recreated the issue in a dummy example here:
#include <string>
#include <iostream>
#include <cstdio>
// Pull the string value after the equals sign
std::string get_option( std::string input );
// Verify that the input is a valid option
bool is_valid_option( std::string input );
int main ( int argc, char** argv )
{
if ( argc != 2 )
{
std::cerr << "Takes exactly two arguments. You gave " << argc << "." << std::endl;
exit( -1 );
}
// Convert from char* to string
std::string arg ( argv[1] );
if ( !is_valid_option( arg ) )
{
std::cerr << "Argument " << arg << " is not a valid option of the form --<argument>=<option>." << std::endl;
exit( -2 );
}
std::cout << "You entered: " << arg << std::endl;
std::cout << "The option you wanted to use is: " << get_option( arg ) << "." << std::endl;
return 0;
}
std::string get_option( std::string input )
{
int index = input.find( '=' );
std::string opt = input.substr( index + 1 ); // We want everything after the '='
return opt;
}
bool is_valid_option( std::string input )
{
int equals_index = input.find('=');
return ( equals_index != std::string::npos && equals_index < input.length() - 1 );
}
I compile like this:
g++ -std=c++11 dummy.cpp -o dummy
With the following commands, it produces the following outputs.
With double quotes:
/dummy --option="\t"
You entered: --option=\t
The option you wanted to use is: \t.
With single quotes:
./dummy --option='\t'
You entered: --option=\t
The option you wanted to use is: \t.
With no quotes:
./dummy --option=\t
You entered: --option=t
The option you wanted to use is: t.
My question is: Is there a way to specify that it should interpret the substring \t as a tab character (or other escape sequences) rather than the string literal "\t"? I could parse it manually, but I'm trying to avoid re-inventing the wheel when I might just be missing something small.
Thank you very much for your time and answers. This is something so simple that it's been driving me crazy that I'm not sure how to fix it quickly and simply.

The escape sequences are already parsed from the shell you use, and are passed to your command line parameters array argv accordingly.
As you noticed only the quoted versions will enable you to detect that a "\\t" string was parsed and passed to your main().
Since most shells may just skip a real TAB character as a whitespace, you'll never see it in your command line arguments.
But as mentioned it's mainly a problem of how the shell interprets the command line, and what's left going to your program call arguments, than how to handle it with c++ or c.
My question is: Is there a way to specify that it should interpret the substring \t as a tab character (or other escape sequences) rather than the string literal "\t"? I could parse it manually, but I'm trying to avoid re-inventing the wheel when I might just be missing something small.
You actually need to scan for a string literal
"\\t"
within the c++ code.

Related

how to define my own special character in cout

for example :
cout << " hello\n400";
will print:
hello
400
another example:
cout << " hello\r400";
will print:
400ello
there is a option to define my own special character?
i would like to make somthing like:
cout << " hello\d400";
would give:
hello
400
(/d is my special character, and i already got the function to get the stdout cursor one line down(cursorDown()),but i just don't how to define a special character that each time will be writted will call to my cursorDown() function)
As said by others there is no way you can make cout understand user defined characters , however what you could do is
std::cout is an object of type std::ostream which overloads operator<<. You could create an object of the struct which parses your string for your special characters and other user defined characters before printing it to a file or console using ostream similar to any log stream.
Example
or
Instead of calling cout << "something\dsomething"
you can call a method special_cout(std::string); which parses the string for user defined characters and executes the calls.
There is no way to define "new" special characters.
But you can make the stream interpret specific characters to have new meanings (that you can define). You can do this using locals.
Some things to note:
The characters in the string "xyza" is just a simple way of encoding a string. Escaped characters are C++ way of allowing you to represent representing characters that are not visible but are well defined. Have a look at an ASCII table and you will see that all characters in the range 00 -> 31 (decimal) have special meanings (often referred to as control characters).
See Here: http://www.asciitable.com/
You can place any character into a string by using the escape sequence to specify its exact value; i.e. \x0A used in a string puts the "New Line" character in the string.
The more commonly used "control characters" have shorthand versions (defined by the C++ language). '\n' => '\x0A' but you can not add new special shorthand characters as this is just a convenience supply by the language (its like a tradition that most languages support).
But given a character can you give it a special meaning in an IO stream. YES. You need to define a facet for a locale then apply that locale to the stream.
Note: Now there is a problem with applying locals to std::cin/std::out. If the stream has already been used (in any way) applying a local may fail and the OS may do stuff with the stream before you reach main() and thus applying a locale to std::cin/std::cout may fail (but you can do it to file and string streams easily).
So how do we do it.
Lets use "Vertical Tab" as the character we want to change the meaning of. I pick this as there is a shortcut for it \v (so its shorter to type than \x0B) and usually has no meaning for terminals.
Lets define the meaning as new line and indent 3 spaces.
#include <locale>
#include <algorithm>
#include <iostream>
#include <fstream>
class IndentFacet: public std::codecvt<char,char,std::mbstate_t>
{
public:
explicit IndentFacet(size_t ref = 0): std::codecvt<char,char,std::mbstate_t>(ref) {}
typedef std::codecvt_base::result result;
typedef std::codecvt<char,char,std::mbstate_t> parent;
typedef parent::intern_type intern_type;
typedef parent::extern_type extern_type;
typedef parent::state_type state_type;
protected:
virtual result do_out(state_type& tabNeeded,
const intern_type* rStart, const intern_type* rEnd, const intern_type*& rNewStart,
extern_type* wStart, extern_type* wEnd, extern_type*& wNewStart) const
{
result res = std::codecvt_base::ok;
for(;(rStart < rEnd) && (wStart < wEnd);++rStart,++wStart)
{
if (*rStart == '\v')
{
if (wEnd - wStart < 4)
{
// We do not have enough space to convert the '\v`
// So stop converting and a subsequent call should do it.
res = std::codecvt_base::partial;
break;
}
// if we find the special character add a new line and three spaces
wStart[0] = '\n';
wStart[1] = ' ';
wStart[2] = ' ';
wStart[3] = ' ';
// Note we do +1 in the for() loop
wStart += 3;
}
else
{
// Otherwise just copy the character.
*wStart = *rStart;
}
}
// Update the read and write points.
rNewStart = rStart;
wNewStart = wStart;
// return the appropriate result.
return res;
}
// Override so the do_out() virtual function is called.
virtual bool do_always_noconv() const throw()
{
return false; // Sometime we add extra tabs
}
};
Some code that uses the locale.
int main()
{
std::ios::sync_with_stdio(false);
/* Imbue std::cout before it is used */
std::cout.imbue(std::locale(std::locale::classic(), new IndentFacet()));
// Notice the use of '\v' after the first lien
std::cout << "Line 1\vLine 2\nLine 3\n";
/* You must imbue a file stream before it is opened. */
std::ofstream data;
data.imbue(std::locale(std::locale::classic(), new IndentFacet()));
data.open("PLOP");
// Notice the use of '\v' after the first lien
data << "Loki\vUses Locale\nTo do something silly\n";
}
The output:
> ./a.out
Line 1
Line 2
Line 3
> cat PLOP
Loki
Uses Locale
To do something silly
BUT
Now writing all this is not really worth it. If you want a fixed indent like that us a named variable that has those specific characters in it. It makes your code slightly more verbose but does the trick.
#include <string>
#include <iostream>
std::string const newLineWithIndent = "\n ";
int main()
{
std::cout << " hello" << newLineWithIndent << "400";
}

escaping a variable into a quote?

i'm trying to make a function that outputs n amount of colored spaces or " ". I specifically need it to be a quote string thing (not s) because I'm sending as an argument to the system() function.
is there a way that I can use a char variable inside a string.
I need it to function like this(I know it won't work):
system("echo -e \"\e[45m _myCharHere_ \"");
this way I can make the spaces (" ") any size I choose by multiplying the char by what ever integer I choose.
This is probably a stupid question, but I don't know all the technical programming terms for these operations etc; so I can't google it.
Thanks
You may use something like:
// Assuming mychar doesn't need escape sequence.
std::string command = std::string("echo -e \"\e[45m ") + myChar + " \"";
system(command.c_str());
Or as mentioned in comment, write directly (and here myChar can be special char):
std::cout << "\033[45m " << myChar;

How to match absolute value using regex

I am having trouble with absolute value in regex in C++. This is what I have as the pattern:
std::tr1::regex loadAbsNM("load -|M\\((\\d+)\\)|"); // load -|M(x)|
I am trying to use std::tr1::regex_match( IR, result, loadNM ) to match. But it is not matching anything, even though it should be.
I'm using Visual Stuido 2010 compilier
shortened version of program (included above is iostream and regex)
int main()
{
std::string IR = "load -|M(x)|";
std::smatch result;
std::tr1::regex loadAbsNM("load -|M\\((\\d+)\\)|");
if( std::tr1::regex_match( IR , result, loadAbsNM ) )
{
int x = 2;
std::cout << "matched!" << std::endl;
}
else
{
std::cout << "!UNABLE TO DECODE INSTRUCTION!" << std::endl;
}
}
output produced
!UNABLE TO DECODE INSTRUCTION!
Note that from your code, you're not going to have a match. The letter x won't match the regex \d+.
Also, I'm not too sure whether you need a backslash in front of the pipe character. As you may know, pipe (|) is used to separate possible entries: (a|b) means a or b.
Finally, since their is a pipe at the end, the expression matches the empty string which is often a bad idea.
I would suggest something like this:
"load -\\|M\\((\\d+)\\)\\|"
But that won't match:
"load -|M(x)|"
You'd need to use a number instead of 'x' as in:
"load -|M(123)|"

Regular expressions in c++11

I want to parser cpu info in Linux. I wrote such code:
// Returns full data of the file in a string
std::string filedata = readFile("/proc/cpuinfo");
std::cmath results;
// In file that string looks like: 'model name : Intel ...'
std::regex reg("model name: *");
std::regex_search(filedata.c_str(), results, reg);
std::cout << results[0] << " " << results[1] << std::endl;
But it returns empty string. What's wrong?
Not all compilers support the full C++11 specification yet. Notably, regex_search does not work in GCC (as of version 4.7.1), but it does in VC++ 2010.
You didn't specify any capture in your expression.
Given the structure of /proc/cpuinfo, I'd probably prefer a line
oriented input, using std::getline, rather than trying to do
everything at once. So you'ld end up with something like:
std::string line;
while ( std::getline( input, line ) ) {
static std::regex const procInfo( "model name\\s*: (.*)" );
std::cmatch results;
if ( std::regex_match( line, results, procInfo ) ) {
std::cout << "???" << " " << results[1] << std::endl;
}
}
It's not clear to me what you wanted as output. Probably, you also
have to capture the processor line as well, and output that at the
start of the processor info line.
The important things to note are:
You need to accept varying amounts of white space: use "\\s*" for 0 or more, "\\s+" for one or more whitespace characters.
You need to use parentheses to delimit what you want to capture.
(FWIW: I'm actually basing my statements on boost::regex, since I
don't have access to std::regex. I think that they're pretty similar,
however, and that my statements above apply to both.)
Try std::regex reg("model_name *: *"). In my cpuinfo there are spaces before colon.

C++ printf: newline (\n) from commandline argument

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