5

I'm trying to check against multiple possibilities in an if statement.

The user inputs a string, and then I check that string against multiple possibilities.

if (theString == "Seven" || "seven" || "7")
 {
   theInt = 7;
   cout << "You chose: " << theInt << endl;
 }
else if (theString == "Six" || "six" || "6")
 {
   theInt = 6;
   cout << "You chose: " << theInt << endl;
 }

So there's just a quick example of what I'm trying to accomplish. Any ideas?

3
  • 1
    if (theString == "Seven" || theString == "seven" || theString == "7") Commented Apr 19, 2017 at 1:29
  • 1
    What's the type of theString? Commented Apr 19, 2017 at 1:31
  • Create an array of the strings you want to compare to. Then in a loop you compare each one. Compared to your solution it's scalable too! Commented Feb 8, 2023 at 14:01

6 Answers 6

8

I suppose that the type of the variable theString is std::string. Otherwise at least this comparison

theString == "Seven"

does not make sense,

The condition in the if statement

if (theString == "Seven" || "seven" || "7")

is equivalent to

if ( ( theString == "Seven" ) || ( "seven" ) || ( "7" ) )

and always yields true because at least the address of the string literal "seven" is not equal to zero. So this subexpression ( "seven" ) provides that the whole expression will be equal to true.

You should write

if (theString == "Seven" || theString == "seven" || theString == "7")

But it would be better at first to convert the string to upper or lower case.

For example

#include <algorithm>
#include <string>
#include <cstring>

//...

std::transform(theString.begin(), theString.end(), theString.begin(),
    [](char c) { return std::toupper((unsigned char)c);  });

if (theString == "SEVEN" || theString == "7")
{
    theInt = 7;
    cout << "You chose: " << theInt << endl;
}
else if ( theString == "SIX" || theString == "6" )
{
    theInt = 6;
    cout << "You chose: " << theInt << endl;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Ugly solution. Don't teach our youngster this way! Use an array and a loop!
@Johnny Are you one of "our youngster"?:) The answer answers the question "How to compare multiple strings inside an if statement?". I am sure that the answer is good.
6

Using std::set and c++11 you can do one liner with similar syntax as your.

check this:

#include <iostream>
#include <string>
#include <set>

int main()
{
  
  if( (std::set<std::string>{"Seven", "seven", "7"}).count("seven") )
  {
      std::cout << "foo\n";
  }
  
  std::string theString("6");
  
  if( (std::set<std::string>{"Six", "six", "6"}).count(theString) )
  {
      std::cout << "bar\n";
  }
}

Note: from c++20 you can use contains instead of count

Comments

3

You cannot compare a variable against multiple values like that in C++. You should be doing:

if (theString == "Seven" || theString == "seven" || theString ==  "7")
 {
   theInt = 7;
   cout << "You chose: " << theInt << endl;
 }
else if (theString == "Six" || theString == "six" || theString == "6")
 {
   theInt = 6;
   cout << "You chose: " << theInt << endl;
 }

Comments

2

Sometimes, data can be a better solution than code.

std::map<std::string, int> values;
values["Seven"]=7;
values["seven"]=7;
values["7"]=7;
values["Six"]=6;
values["six"]=6;
values["6"]=6;

std::string input;
std::cin >> input;
std::cout << values[input];

As Vlad noted, you might be better of converting to lowercase first. This answer is just the straightforward conversion of your code to data. Note that this answer will use a default value of 0 for missing strings; your code left out such a default.

2 Comments

I actually ended up learning about <map> and applying it later in my code. The way I understood it was - I converted all my strings into "simple" data types, using enum? And then I used a switch statement to check all of those data types to determine what action to take. I ended up having 40+ enum values. I might have to look more into what you just did, where multiple strings equal the same value. I have 1 class of C++ under my belt, so this is all beyond what I know, but it's great! Thanks for the help!
@Jveto: Maps are containers of key-value pairs. You can (within reason) pick any key and value type, as long as the keys can be sorted. Sorting is necessary so the container elements (the pairs) are ordered by their key. It also makes it efficient to find a value given just the key. Strings are alphabetically ordered so they can be used in maps, and I've used that here. The value I associate with each string key is the numerical value, so an int. But you can also use enum values, or colors, or times, or ...
2

This is a classic example of identifying a derived requirement found during implementation. I suggest you consider writing a function to support it.

change from

if (theString == "Seven" || "seven" || "7")
{
//....

(which is not valid c++ because the if condition is always true)

change to

if (0 == compare(theString, "Seven", "seven", "7")
{
//....

and declare and implement something like

// return 0 when theString matches at least one patX
// else return -1
int compare(const std::string& theString, 
            const char* pat0,  // no default, must provide
            const char* pat1 = nullptr, 
            const char* pat2 = nullptr, 
            const char* pat3 = nullptr, 
            const char* pat4 = nullptr
            /* add as many patX as you desire */)
{
    if (0 == theString.compare(pat0)) return 0; // found a match
    //
    // ignore nullptr patterns 
    if (nullptr != pat1) && (0 == theString.compare(pat1)) {
       return(0);
    }

    if(nullptr != pat2) && (0 == theString.compare(pat2)) {
       return(0);
    }
    // ...

    // when get to end with no match
    return (-1); // indicate no match to any of patterns
}

I actually prefer the following. The above is somewhat like strstr(), where this uses more functionality of std::string

int compare(const std::string& theString, // what you are given
            const std::string& patterns)  // concatenated list of search patterns
{
//.. 
}

This you invoke as

if (0 == compare(theString, "Seven seven SEVEN 7") 
{
// 

The implementation must tease apart space delimited patterns .. but this is not difficult, and can easily be implemented in a loop, so no limit to how many comparisons you wish to test.


When should you consider creating a new function in support of a new derived rerquirement?

It is my practice to create the new function when I can identify 3 or more uses. Good luck.


Found some code I wrote a few years ago, fixed it up, added demos ...

Code compiles and seems to run, but very little testing.

  • I created minimal packaging -- a dummy namepace (dtb - for d___'s tool box), and a dumy class (T471_t - test 471)

dtb::T471_t provides private methods for your review.

  • size_t compare(const std::string s, std::string patterns)

  • size_t grep(const std::string pfn, const std::string patterns, std::ostream& an_ostream = std::cout)

    -- uses compare()

  • size_t cpuinfoGet()

    -- uses grep

    -- "wc < /proc/cpuinfo" (a 'well known file' on ubuntu) reports 54 lines on my 2 core machine, more cores, more lines

  • size_t coreCountGet()

    -- uses grep()

    -- creates nullDev to suppress grep normal output


#include <chrono>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cassert>

// 'compressed' chrono access --------------vvvvvvv
typedef std::chrono::high_resolution_clock  HRClk_t; // std-chrono-hi-res-clk
typedef HRClk_t::time_point                 Time_t;  // std-chrono-hi-res-clk-time-point
typedef std::chrono::microseconds           US_t;    // std-chrono-microseconds
using   namespace std::chrono_literals;   // support suffixes like 100ms

// examples:
//
//   Time_t start_us = HRClk_t::now();
//
//   auto  duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
//   auto     count_us = duration_us.count();
//   or
//   std::cout << "  complete " << duration_us.count() << " us" << std::endl;

namespace dtb
{
   class T471_t
   {
      const std::string dashLine = ("  --------------------------------------------------------------");
   public:

      T471_t() = default;
      ~T471_t() = default;

      int exec()
         {
            std::cout << "\n  cpuinfoGet()\n" << dashLine << std::endl;

            (void)cpuinfoGet(); // uses grep which uses compare

            std::cout << dashLine << std::endl;

            // count of lines which contain "processor" in linux file "/proc/cpuinfo"
            std::cout << "\n\n  " << coreCountGet()
                      << " cores on this system. (coreCountGet())\n\n" << std::endl;

            return(0);
         }


   private: // methods

      // returns std::string::npos when none of the patterns found,
      // else returns index of earliest found patterns of space delimited substr in
      size_t compare (const std::string& s,
                      std::string        patterns) // pass by value
         {
            size_t    found = std::string::npos;
            size_t patCount = 0;
            std::stringstream ssPat(patterns + ' '); // load patterns into ram based stringstream
            //                            ^^^^^ -- getline() can cause eof() in this
            const char delim = ' '; // see getline()

            do
            {
               if(0 == patterns.size()) break; // no patterns to search for, kick out
               if(0 == s.size())     break; // no string in which to search, kick out

               do {
                  std::string pat;
                  (void)std::getline(ssPat, pat, delim); // extract 1 space delimited pattern

                  if(false == ssPat.good())
                  {
                     if(ssPat.eof()) break; // quitely exit, a normal op

                     // let user know of patten problem
                     std::cerr << "\n  err pattern extract: " << patterns
                               << "  (" << patCount << ')' << std::endl;
                     break;
                  }
                  patCount += 1;

                  //trimLeadingWhiteSpace(patterns);  // tbr
                  //trimTrailingWhiteSpace(patterns); // tbr

                  if(0 == patterns.size()) break; // no more patterns

                  // search s for pat
                  found = s.find(pat);

                  if(found != std::string::npos) break; // one of the patterns found in s

               } while(1); // repeat until 1 found or all patterns tried

            }while(0);

            return(found);
         } // size_t compare (const std::string& s, std::string patterns)



      size_t grep(const std::string pfn,
                  const std::string patterns, // concatenated list of search patterns
                  std::ostream&     an_ostream = std::cout) // redirectable
         {
            size_t   foundCount = 0;

            an_ostream << "  grep (" << pfn << ", [" << patterns
                       << "] )" << std::endl;
            do
            {
               std::ifstream infile(pfn);

               if(!infile.is_open())
               {
                  an_ostream << pfn << " not found.\n" << std::endl; // tbr - std::cerr?
                  break; // skip over file op's (no close needed)
               }

               do
               {
                  if(infile.eof()) break;  // file empty?

                  std::string lineIn;
                  (void)getline(infile, lineIn); // default delimiter is \n

                  if (0 == lineIn.size()) continue; // empty line?

                  size_t found = compare(lineIn, patterns); // any of patterns in lineIn?

                  if(std::string::npos != found) // found at least one pattern
                  {
                     an_ostream << "  " << lineIn << std::endl; // found it, print it
                     foundCount += 1;
                  }
                  // else no pattern found - continue until eof of inFil

               } while(1);

               infile.close();

            }while(0);

            return(foundCount);
         } // size_t grep(const std::string pfn, const std::string patterns, std::ostream& an_ostream = std::cout)
      //                                space delimited list of ---^^^^^^^^



      size_t cpuinfoGet()
         {
            size_t count = grep("/proc/cpuinfo",  // pfn
                                "bogomips model name processor"); // patterns to search for
            std::cout << "\n  info lines: " << count << std::endl;
            return(count);
         } // size_t cpuinfoGet(void)



      size_t coreCountGet()
         {
            // create a no-ouptput output
            std::ofstream nullDev; // and do not open
            nullDev.setstate(std::ios_base::badbit); // set error, ignore errors, do not close

            size_t retVal = grep(std::string("/proc/cpuinfo"),
                                 std::string("processor"),  // line count of "processor" is core count
                                 nullDev); // nullDev -- no output
            return(retVal);
         } // size_t coreCountGet()(void)

   }; // class T471_t
} // namespace dtb


int main(int /*argc*/, char** /*argv[]*/)
{
  Time_t start_us = HRClk_t::now();

  int retVal = -1;
  {
     dtb::T471_t  t471;
     retVal = t471.exec();
  }

  auto  duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);

  std::cout << "  FINI   " << duration_us.count() << " us" << std::endl;
  return(retVal);
}

Output:

cpuinfoGet()
--------------------------------------------------------------
grep (/proc/cpuinfo, [bogomips model name processor] )
processor   : 0
model       : 75
model name  : AMD Athlon(tm) 64 X2 Dual Core Processor 5000+
bogomips    : 4809.67
processor   : 1
model       : 75
model name  : AMD Athlon(tm) 64 X2 Dual Core Processor 5000+
bogomips    : 4809.67

info lines: 8
--------------------------------------------------------------


 2 cores on this system. (coreCountGet())


 FINI   829 us

2 Comments

"not valid c++" - actually it is valid C++. That's unfortunate, but required for backwards compatibility with C. As for your suggested alternatives, I would call this outright bad design. From using int as a boolean type to arbitrarily limiting the type and number of arguments, it's just ignoring the improvements that C++ offers over C. And using spaces as a runtime separator for strings is really bad, what if your input contains a space itself?
@MSalters - thanks for the feedback. I suppose you mean it will compile, but always returns true. I think this does make it invalid, and besides, it is a typical mistake. something most of us recognize for the obvious reasons. I admit that I can not remember ever having used this 'bad' design in production code. I just guessed it to be what he wants to do, for now. More practice may change his mind. My imagination often fails me. The int return code is from the C++ std::string::compare method.
0

Avoid hard-coded strings in code. This example is scalable. You can easily add strings and their corresponding values in just one place.

typedef struct
{
    const char *oneString;
    int value;
} THESTRINGS;

THESTRINGS theStrings[]
{
    "seven", 7,
    "six", 6
};
#define STRINGCOUNT (sizeof(theStrings) / sizeof(theStrings[0]))

int main()
{
    int i;
    const char* theString = "SIX";

    for (i = 0; i < STRINGCOUNT; i++)
    {
        if (_stricmp(theString, theStrings[i].oneString) == 0)              // Case does not matter with _stricmp()
        {
            printf("Found %s and its value %d", theString, theStrings[i].value);
            break;
        }
    }
    return 0; // Prints "Found SIX and its value 6"
}

3 Comments

Is this solution for pure C? If not probably using std::map would be a better choice. Also there is initialization of a global object that potentially may cause some issues. Using global function/static method that would return a ref to a static object may be a better solution.
It is one working Minimal, Complete, and Verifiable example.
ah and again if it is not C but c++ then typedef struct ... THESTRINGS is somewhat not necessary just use struct THESTRINGS

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.