I want to compare two strings in C++:
There is a function getName() that returns a string.
Now I can write Out << getName(); this will print the string.
However I want to print the string only if its value is arpit or arpit*N* where N is an integer. I don't want to print it if its value is arpita, arpitx, where N is something other than an integer or an empty string.
I know this can be easily done, but I want to do this in a minimal number of lines.
What I have done so far is:
char name1[] = getName();
char name2[] = "arpit";
for (int x = 0; x <= 4; x++){
if (name1[x] == name2[x]) continue;
else return ( Out << "not equal") ;
}
while(name1[x] ! = "\0"){
if(isdigit(name1[x])
x++;
else return (Out << "not equal") ;
}
Out << getName();
UPDATE 1
getName() returns a string until it encounters white space, and it will not return any line or 2 or more words.
If you have C++11:
static std::regex const matcher( "arpit\\d*" );
if ( regex_match( name, matcher ) ) {
// matches...
}
If you don't have C++11, boost::regex is practically identical.
If you don't have C++11, and you can't use boost:
if ( name1.size() >= name2.size()
&& std::equal( name2.begin(), name2.end(), name1.begin() )
&& std::find_if( name1.begin() + name2.size(),
name1.end(),
[]( unsigned char ch ) { return !isdigit( ch ); }
) == name1.end() )
// matches...
}
For the rest, your code has quite a few errors, and shouldn't
compile. In particular, there is nothing which getName()
could return which can be used to initialize a char []; the
type of a string in C++ is std::string, and your variables
should be:
std::string name1( getName() );
std::string name2( "arpit" );
(except that you need better names. The second might be
something like reference or header, for example.)
And of course, it's undefined behavior to call isdigit with
a char; you have to convert to unsigned char first.
auto s = getName();
if(s.size() >= 5 && s.substr(0,5) == "arpit")
Out << s;
std::set<std::string> m_allowed_strings;
std::string validate_string(const string & s)
{
if(m_allowed_strings.find(s) != m_allowed_strings.end())
return s;
return "";
}
cout << validate_string(getName());
Related
I've been given a class smartReverse which contains one member data which is a string called str. I have to implement a member method (without any sort of helper function which takes no parameters and returns the reversed version of str.
This is my attempt so far but this does nothing but send the first character to the end of the string. And from this point I'm pretty clueless. I know how to do this using a helper function but am not allowed to use them here.
string smartReverse::rev_recursive() const
{
if (str.length() <= 1)
return str;
char first_char = str[0];
smartReverse* remainder = new smartReverse(str.substr(1));
remainder->rev_recursive();
return remainder->getString() + first_char;
}
With memleak removed, and using rev_recursive result, the fixed version might be:
std::string smartReverse::rev_recursive() const
{
if (str.length() <= 1) {
return str;
}
char first_char = str[0];
smartReverse remainder(str.substr(1));
return remainder.rev_recursive() + first_char;
}
There is no any need to allocate an object of the type std::string dynamically. It is just a bad idea.
I do not know how the class smartReverse looks but here is its simplified version that has only one member function rev_recursive that reverses the stored string.
#include <iostream>
#include <string>
class smartReverse
{
public:
smartReverse( const std::string &s ) : s( s ) {}
std::string rev_recursive()
{
if ( s.size() < 2 ) return s;
char first = s.front(), last = s.back();
s = s.substr( 1, s.size() - 2 );
return s = last + rev_recursive() + first;
}
private:
std::string s;
};
int main()
{
smartReverse obj( "Hello Brennen Green" );
std::cout << obj.rev_recursive() << '\n';
return 0;
}
The program output is
neerG nennerB olleH
If the function shall be a constant member function then its implementation can look the following way
#include <iostream>
#include <string>
class smartReverse
{
public:
smartReverse( const std::string &s ) : s( s ) {}
std::string getString() const
{
return s;
}
std::string rev_recursive() const
{
if ( s.size() < 2 ) return s;
char first = s.front(), last = s.back();
return last + smartReverse( s.substr( 1, s.size() - 2 ) ).rev_recursive() + first;
}
private:
std::string s;
};
int main()
{
smartReverse obj( "Hello Brennen Green" );
std::cout << obj.getString() << '\n';
std::cout << obj.rev_recursive() << '\n';
return 0;
}
The program output is
Hello Brennen Green
neerG nennerB olleH
Pay attention to that the used approach is more efficient than when only one character is removed from the beginning of the string and then appended to the end because in the used approach the number of recursions is less than or equal to s.size() / 2 of the original string.
(C++)
Given myString, I want to check if myString contains substring. Here's what I have so far, but it only returns true if the string begins with the substring.
bool find(string myString, string substring)
{
if(mystring.length() < substring.length())
{
return false;
}
if(mystring == substring)
{
return true;
}
for(int i = 0; i < substring.length() - 1 ; ++i)
{
if(mystring.at(i) == substring.at(i))
{
continue;
}
else
{
string string2 = mystring.substr(1, mystring.length() - 1);
return find(string2, substring);
}
return true;
}
return false;
}
What is wrong with this function?
Check this function, it based on your code, with removal of extra code and fix of the errors.
I also changed the signature to get const reference to improve the efficiency.
bool find(const string& myString, const string& substring)
{
if(myString.length() < substring.length()){
return false;
}
else if(myString.substr(0,substring.size()) == substring){
return true;
}
else if (myString.length() > substring.length()){
return find(myString.substr(1), substring);
}
else{
return false;
}
}
First of all the function can be written simpler. For example
bool find( const std::string &myString, const std::string &subString )
{
return
( myString.substr( 0, subString.size() ) == subString ) ||
( subString.size() < myString.size() && find( myString.substr( 1 ), subString ) );
}
Here is a demonstrative program
#include <iostream>
#include <iomanip>
#include <string>
bool find( const std::string &myString, const std::string &subString )
{
return
( myString.substr( 0, subString.size() ) == subString ) ||
( subString.size() < myString.size() && find( myString.substr( 1 ), subString ) );
}
int main()
{
std::cout << std::boolalpha << find( "Hello World", "World" ) << std::endl;
std::cout << std::boolalpha << find( "Hello C", "C++" ) << std::endl;
}
The program output is
true
false
As for your function then it will return true only in the case when the both string have the same length and are equal each other
if(myString == substring){
return true;
}
And in case when myString.length() > substring.length() the function returns nothing
else if (myString.length() > substring.length()){
int start = 1;
int end = (int) myString.length() - 1;
string string2 = myString.substr(start, end);
find(string2, substring);
}
I think you mean
return find(string2, substring);
in this code snippet.
EDIT: I see that you changed the code of the function in your post. But in any case this code snippet
for(int i = 0; i < substring.length() - 1 ; ++i)
{
if(mystring.at(i) == substring.at(i))
{
continue;
}
else
{
string string2 = mystring.substr(1, mystring.length() - 1);
return find(string2, substring);
}
return true;
}
makes no sense.
You're missing a return before the recursive call to find. As it stands it falls through to the return false at the end.
Also, if (mystring == substring) should be checking if mystring starts with substring, not exact equality.
First, this is expensive because of the memory copies in substr.
Second, you havent checked for substring length > 0.
Third, the "else if" check for mystring.length > 0 is redundant if you have done the other checks (including substring length > 0).
Now to your core logic. In the recursion your start is never moving, so you are tied to the beginning. What you need to do is start with position 1, and increment start at every recursion, and also extract using substr the substring from "start" to "start + substring.length". That way you start from the beginning, keep moving forward, and check the correct length. You could also start from the end (as you have) and move back, what you would have to do there is: find sart position (end position minus length of substring), and check that the start position is not less than zero before calling the function recursively.
You're just removing the leftmost characters of myString and then comparing the rest to your substring. Obviously, this is not going to work in a general case, when your substring is somewhere in the middle of myString.
On each iteration try comparing not the whole myString, but rather the first substring.size() characters of it. This should fix your issue.
Here's what I have so far, but it only returns true if the string
begins with the substring.
It also fails for find("foo", "f").
To see why, add some test output to the function:
bool find(string myString, string substring)
{
std::cout << myString << ", " << substring << "\n";
// ...
}
It will print:
foo, f
oo, f
o, f
You see why this cannot work? You just keep removing the first character, until only the last character is compared with the substring to be found.
But it fails even for find("foo", "o"):
foo, o
oo, o
o, o
That's because of this line:
find(string2, substring);
You don't return the result of the recursive call.
All things considered, I think you just have the wrong algorithm here. It simply cannot work the way you have written the code.
A few other observations:
int start = 1;
int end = (int) myString.length() - 1;
That's not good style. For historical reasons, a std::string's size is unsigned, and you are using a C-style cast where static_cast should be preferred. You should just use std::string::size_type here, because it's just an internal piece of implementation code and you gain nothing from casting to int.
string string2 = myString.substr(start, end);
The second argument of substr defines the length of the substring, not the index of the last character. end sounds like you use the value as the index of the last character. Have a look at http://en.cppreference.com/w/cpp/string/basic_string/substr.
I have a pattern that in the following format:
AUTHOR, "TITLE" (PAGES pp.) [CODE STATUS]
For example, I have a string
P.G. Wodehouse, "Heavy Weather" (336 pp.) [PH.409 AVAILABLE FOR LENDING]
I want to extract
AUTHOR = P.G. Wodehouse
TITLE = Heavy Weather
PAGES = 336
CODE = PH.409
STATUS = AVAILABLE FOR LENDING
I only know how to do that in Python, however, are there any efficient way to do the same thing in C++?
Exactly the same way as in Python. C++11 has regular expressions (and for earlier C++, there's Boost regex.) As for the read loop:
std::string line;
while ( std::getline( file, line ) ) {
// ...
}
is almost exactly the same as:
for line in file:
# ...
The only differences are:
The C++ version will not put the trailing '\n' in the buffer. (In general, the C++ version may be less flexible with regards to end of line handling.)
In case of a read error, the C++ version will break the loop; the Python version will raise an exception.
Neither should be an issue in your case.
EDIT:
It just occurs to me that while regular expressions in C++ and in Python are very similar, the syntax for using them isn't quite the same. So:
In C++, you'd normally declare an instance of the regular expression before using it; something like Python's re.match( r'...', line ) is theoretically possible, but not very idiomatic (and it would still involve explicitly constructuing a regular expression object in the expression). Also, the match function simply returns a boolean; if you want the captures, you need to define a separate object for them. Typical use would probably be something like:
static std::regex const matcher( "the regular expression" );
std::smatch forCaptures;
if ( std::regex_match( line, forCaptures, matcher ) ) {
std::string firstCapture = forCaptures[1];
// ...
}
This corresponds to the Python:
m = re.match( 'the regular expression', line )
if m:
firstCapture = m.group(1)
# ...
EDIT:
Another answer has suggested overloading operator>>; I heartily concur. Just out of curiousity, I gave it a go; something like the following works well:
struct Book
{
std::string author;
std::string title;
int pages;
std::string code;
std::string status;
};
std::istream&
operator>>( std::istream& source, Book& dest )
{
std::string line;
std::getline( source, line );
if ( source )
{
static std::regex const matcher(
R"^(([^,]*),\s*"([^"]*)"\s*\((\d+) pp.\)\s*\[(\S+)\s*([^\]]*)\])^"
);
std::smatch capture;
if ( ! std::regex_match( line, capture, matcher ) ) {
source.setstate( std::ios_base::failbit );
} else {
dest.author = capture[1];
dest.title = capture[2];
dest.pages = std::stoi( capture[3] );
dest.code = capture[4];
dest.status = capture[5];
}
}
return source;
}
Once you've done this, you can write things like:
std::vector<Book> v( (std::istream_iterator<Book>( inputFile )),
(std::istream_iterator<Book>()) );
And load an entire file in the initialization of a vector.
Note the error handling in the operator>>. If a line is misformed, we set failbit; this is the standard convention in C++.
EDIT:
Since there's been so much discussion: the above is fine for small, one time programs, things like school projects, or one time programs which will read the current file, output it in a new format, and then be thrown away. In production code, I would insist on support for comments and empty lines; continuing in case of error, in order to report multiple errors (with line numbers), and probably continuation lines (since titles can get long enough to become unwieldly). It's not practical to do this with operator>>, if for no other reason than the need to output line numbers, so I'd use a parser along the following line:
int
getContinuationLines( std::istream& source, std::string& line )
{
int results = 0;
while ( source.peek() == '&' ) {
std::string more;
std::getline( source, more ); // Cannot fail, because of peek
more[0] = ' ';
line += more;
++ results;
}
return results;
}
void
trimComment( std::string& line )
{
char quoted = '\0';
std::string::iterator position = line.begin();
while ( position != line.end() && (quoted != '\0' || *position == '#') ) {
if ( *position == '\' && std::next( position ) != line.end() ) {
++ position;
} else if ( *position == quoted ) {
quoted = '\0';
} else if ( *position == '\"' || *position == '\'' ) {
quoted = *position;
}
++ position;
}
line.erase( position, line.end() );
}
bool
isEmpty( std::string const& line )
{
return std::all_of(
line.begin(),
line.end(),
[]( unsigned char ch ) { return isspace( ch ); } );
}
std::vector<Book>
parseFile( std::istream& source )
{
std::vector<Book> results;
int lineNumber = 0;
std::string line;
bool errorSeen = false;
while ( std::getline( source, line ) ) {
++ lineNumber;
int extraLines = getContinuationLines( source, line );
trimComment( line );
if ( ! isEmpty( line ) ) {
static std::regex const matcher(
R"^(([^,]*),\s*"([^"]*)"\s*\((\d+) pp.\)\s*\[(\S+)\s*([^\]]*)\])^"
);
std::smatch capture;
if ( ! std::regex_match( line, capture, matcher ) ) {
std::cerr << "Format error, line " << lineNumber << std::endl;
errorSeen = true;
} else {
results.emplace_back(
capture[1],
capture[2],
std::stoi( capture[3] ),
capture[4],
capture[5] );
}
}
lineNumber += extraLines;
}
if ( errorSeen ) {
results.clear(); // Or more likely, throw some sort of exception.
}
return results;
}
The real issue here is how you report the error to the caller; I suspect that in most cases, and exception would be appropriate, but depending on the use case, other alternatives may be valid as well. In this example, I just return an empty vector. (The interaction between comments and continuation lines probably needs to be better defined as well, with modifications according to how it has been defined.)
Your input string is well delimited so I'd recommend using an extraction operator over a regex, for speed and for ease of use.
You'd first need to create a struct for your books:
struct book{
string author;
string title;
int pages;
string code;
string status;
};
Then you'd need to write the actual extraction operator:
istream& operator>>(istream& lhs, book& rhs){
lhs >> ws;
getline(lhs, rhs.author, ',');
lhs.ignore(numeric_limits<streamsize>::max(), '"');
getline(lhs, rhs.title, '"');
lhs.ignore(numeric_limits<streamsize>::max(), '(');
lhs >> rhs.pages;
lhs.ignore(numeric_limits<streamsize>::max(), '[');
lhs >> rhs.code >> ws;
getline(lhs, rhs.status, ']');
return lhs;
}
This gives you a tremendous amount of power. For example you can extract all the books from an istream into a vector like this:
istringstream foo("P.G. Wodehouse, \"Heavy Weather\" (336 pp.) [PH.409 AVAILABLE FOR LENDING]\nJohn Bunyan, \"The Pilgrim's Progress\" (336 pp.) [E.1173 CHECKED OUT]");
vector<book> bar{ istream_iterator<book>(foo), istream_iterator<book>() };
Use flex (it generates C or C++ code, to be used as a part or as the full program)
%%
^[^,]+/, {printf("Autor: %s\n",yytext );}
\"[^"]+\" {printf("Title: %s\n",yytext );}
\([^ ]+/[ ]pp\. {printf("Pages: %s\n",yytext+1);}
..................
.|\n {}
%%
(untested)
Here's the code:
#include <iostream>
#include <cstring>
using namespace std;
string extract (string a)
{
string str = "AUTHOR = "; //the result string
int i = 0;
while (a[i] != ',')
str += a[i++];
while (a[i++] != '\"');
str += "\nTITLE = ";
while (a[i] != '\"')
str += a[i++];
while (a[i++] != '(');
str += "\nPAGES = ";
while (a[i] != ' ')
str += a[i++];
while (a[i++] != '[');
str += "\nCODE = ";
while (a[i] != ' ')
str += a[i++];
while (a[i++] == ' ');
str += "\nSTATUS = ";
while (a[i] != ']')
str += a[i++];
return str;
}
int main ()
{
string a;
getline (cin, a);
cout << extract (a) << endl;
return 0;
}
Happy coding :)
I wrote the below code to check whether a certin string exists in a text or no. The issue is that match() function always returns false even the pattern exists in the text.
int main(){
char *text="hello my name is plapla";
char *patt="my";
cout<<match(patt,text);
system("pause");
return 0;
}
bool match(char* patt,char* text){
int textLoc=0, pattLoc=0, textStart=0;
while(textLoc <= (int) strlen(text) && pattLoc <= (int)strlen(patt)){
if( *(patt+pattLoc) == *(text+textLoc) ){
textLoc= textLoc+1;
pattLoc= pattLoc+1;
}
else{
textStart=textStart+1;
textLoc=textStart;
pattLoc=0;
}
}
if(pattLoc > (int) strlen(patt))
return true;
else return false;
}
Try pattLoc < (int)strlen(patt) in your while loop.
Loop will stop when pattLoc == 2, so you avoid comparing the '\0' of "my" with the ' ' of "hello my name is pala", which set pattloc to 0 and return false.
Or better, use string substr.
The obvious solution is:
bool
match( std::string const& pattern, std::string const& text )
{
return std::search( text.begin(), text.end(),
pattern.begin(), pattern.end() )
!= text.end();
}
This is idiomatic C++, and the way I would expect any C++ programmer to
write it, at least in a professional environment.
If the goal is to learn how to write such a function, then of course,
the above isn't much of a solution. The solution then should be mroe
divide and conquer; there's much too much in match for you to put it
in one function. I'd recommend something like:
bool
startsWith( std::string::const_iterator begin,
std::string::const_iterator end,
std::string const& pattern )
{
return end - begin >= pattern.size()
&& std::equal( pattern.begin(), pattern.end(), begin );
}
bool
match( std::string const& pattern, std::string const& text )
{
std::string::const_iterator current = text.begin();
while ( current != text.end()
&& !startsWith( begin, text.end(), pattern ) ) {
++ current;
}
return current != text.end();
}
This can obviously be improved; for example, there's no point in
continuing in the while loop when the length of the remaining text is
less than the length of the pattern.
And if your professor insists on your using char const* (if he insists
on char*, then he's totally incompetent, and should be fired), this
can easily be rewritten to do so: just replace all calls to begin with
the pointer, and all calls to end with pointer + strlen(pointer).
I have solved the problem:
while(textLoc <= (int) strlen(text) && pattLoc <= (int)strlen(patt))
should be:
while(textLoc < (int) strlen(text) && pattLoc < (int)strlen(patt))
and
if(pattLoc > (int) strlen(patt))
to
if(pattLoc >= (int) strlen(patt))
When I use getline, I would input a bunch of strings or numbers, but I only want the while loop to output the "word" if it is not a number.
So is there any way to check if "word" is a number or not? I know I could use atoi() for
C-strings but how about for strings of the string class?
int main () {
stringstream ss (stringstream::in | stringstream::out);
string word;
string str;
getline(cin,str);
ss<<str;
while(ss>>word)
{
//if( )
cout<<word<<endl;
}
}
Another version...
Use strtol, wrapping it inside a simple function to hide its complexity :
inline bool isInteger(const std::string & s)
{
if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false;
char * p;
strtol(s.c_str(), &p, 10);
return (*p == 0);
}
Why strtol ?
As far as I love C++, sometimes the C API is the best answer as far as I am concerned:
using exceptions is overkill for a test that is authorized to fail
the temporary stream object creation by the lexical cast is overkill and over-inefficient when the C standard library has a little known dedicated function that does the job.
How does it work ?
strtol seems quite raw at first glance, so an explanation will make the code simpler to read :
strtol will parse the string, stopping at the first character that cannot be considered part of an integer. If you provide p (as I did above), it sets p right at this first non-integer character.
My reasoning is that if p is not set to the end of the string (the 0 character), then there is a non-integer character in the string s, meaning s is not a correct integer.
The first tests are there to eliminate corner cases (leading spaces, empty string, etc.).
This function should be, of course, customized to your needs (are leading spaces an error? etc.).
Sources :
See the description of strtol at: http://en.cppreference.com/w/cpp/string/byte/strtol.
See, too, the description of strtol's sister functions (strtod, strtoul, etc.).
The accepted answer will give a false positive if the input is a number plus text, because "stol" will convert the firsts digits and ignore the rest.
I like the following version the most, since it's a nice one-liner that doesn't need to define a function and you can just copy and paste wherever you need it.
#include <string>
...
std::string s;
bool has_only_digits = (s.find_first_not_of( "0123456789" ) == std::string::npos);
EDIT: if you like this implementation but you do want to use it as a function, then this should do:
bool has_only_digits(const string s){
return s.find_first_not_of( "0123456789" ) == string::npos;
}
You might try boost::lexical_cast. It throws an bad_lexical_cast exception if it fails.
In your case:
int number;
try
{
number = boost::lexical_cast<int>(word);
}
catch(boost::bad_lexical_cast& e)
{
std::cout << word << "isn't a number" << std::endl;
}
If you're just checking if word is a number, that's not too hard:
#include <ctype.h>
...
string word;
bool isNumber = true;
for(string::const_iterator k = word.begin(); k != word.end(); ++k)
isNumber &&= isdigit(*k);
Optimize as desired.
Use the all-powerful C stdio/string functions:
int dummy_int;
int scan_value = std::sscanf( some_string.c_str(), "%d", &dummy_int);
if (scan_value == 0)
// does not start with integer
else
// starts with integer
You can use boost::lexical_cast, as suggested, but if you have any prior knowledge about the strings (i.e. that if a string contains an integer literal it won't have any leading space, or that integers are never written with exponents), then rolling your own function should be both more efficient, and not particularly difficult.
Ok, the way I see it you have 3 options.
1: If you simply wish to check whether the number is an integer, and don't care about converting it, but simply wish to keep it as a string and don't care about potential overflows, checking whether it matches a regex for an integer would be ideal here.
2: You can use boost::lexical_cast and then catch a potential boost::bad_lexical_cast exception to see if the conversion failed. This would work well if you can use boost and if failing the conversion is an exceptional condition.
3: Roll your own function similar to lexical_cast that checks the conversion and returns true/false depending on whether it's successful or not. This would work in case 1 & 2 doesn't fit your requirements.
Here is another solution.
try
{
(void) std::stoi(myString); //cast to void to ignore the return value
//Success! myString contained an integer
}
catch (const std::logic_error &e)
{
//Failure! myString did not contain an integer
}
Since C++11 you can make use of std::all_of and ::isdigit:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string_view>
int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[])
{
auto isInt = [](std::string_view str) -> bool {
return std::all_of(str.cbegin(), str.cend(), ::isdigit);
};
for(auto &test : {"abc", "123abc", "123.0", "+123", "-123", "123"}) {
std::cout << "Is '" << test << "' numeric? "
<< (isInt(test) ? "true" : "false") << std::endl;
}
return 0;
}
Check out the result with Godbolt.
template <typename T>
const T to(const string& sval)
{
T val;
stringstream ss;
ss << sval;
ss >> val;
if(ss.fail())
throw runtime_error((string)typeid(T).name() + " type wanted: " + sval);
return val;
}
And then you can use it like that:
double d = to<double>("4.3");
or
int i = to<int>("4123");
I have modified paercebal's method to meet my needs:
typedef std::string String;
bool isInt(const String& s, int base){
if(s.empty() || std::isspace(s[0])) return false ;
char * p ;
strtol(s.c_str(), &p, base) ;
return (*p == 0) ;
}
bool isPositiveInt(const String& s, int base){
if(s.empty() || std::isspace(s[0]) || s[0]=='-') return false ;
char * p ;
strtol(s.c_str(), &p, base) ;
return (*p == 0) ;
}
bool isNegativeInt(const String& s, int base){
if(s.empty() || std::isspace(s[0]) || s[0]!='-') return false ;
char * p ;
strtol(s.c_str(), &p, base) ;
return (*p == 0) ;
}
Note:
You can check for various bases (binary, oct, hex and others)
Make sure you don't pass 1, negative value or value >36 as base.
If you pass 0 as the base, it will auto detect the base i.e for a string starting with 0x will be treated as hex and string starting with 0 will be treated as oct. The characters are case-insensitive.
Any white space in string will make it return false.