1

I'm writing a piece of my code that checks whether what the user has entered is actually one of the valid inputs (1-9 in this case), and will give an error message if it isn't.

This is what I have:

if (input != '1', '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '0' )
    {
      cout << "Error";
    }

But it doesn't seem to work. I thought I could use commas to separate them, but maybe I'm imagining that.

Is the only option to just do:

input != '1' && input != '2' && input != '3' etc etc

I know that method would work, but it seems a bit long winded. Is there a simpler way?

Cros Dude
  • 33
  • 1
  • 1
  • 3
  • Maybe this inspires you: https://stackoverflow.com/a/51408285/1413395 – πάντα ῥεῖ Aug 01 '18 at 10:59
  • 4
    Use `>`/`>=` and `<`/`<=`. Digits are guaranteed to be in sequence `'0'` to `'9'` in any encoding supported by C. (e.g. `if (input < '0' || input > '9')`) –  Aug 01 '18 at 10:59
  • This question would most likely be downvoted and left unanswered for homework copy. But you can always use switch in case "if" has too many options, and give default case as error like switch(input){ case 1: ... default: Error; break; } – Pavan Kate Aug 01 '18 at 11:00
  • 4
    Another option is [`isdigit()`](http://www.cplusplus.com/reference/cctype/isdigit/) in this special case. –  Aug 01 '18 at 11:02
  • @FelixPalmen: But your current locale might differ from your source character set... for example working on EBCDIC input while your source was written / compiled on an ASCII machine... at which point your program would be testing for a range of control characters, while `isdigit()` would still work correctly. – DevSolar Aug 01 '18 at 11:38
  • 1
    @DevSolar this doesn't matter, character literals translate according to the *execution character set*. If **this** doesn't match with the character set actually used on the executing machine, you have much worse problems -- of course, `isdigit()` wouldn't work either. –  Aug 01 '18 at 11:41
  • @FelixPalmen: OK, so I mixed up source and execution character set. But AFAIK they are both *compile-time* sets...?!? And while the standard requires that the integer values for `'0'` through `'9'` must be consecutive, a locale `LC_CTYPE` setting might define a different range for what is `isdigit()` (like ASCII / Latin 0x30..0x3f versus EDCDIC 0xf0..0xf9)? Or am I completely off the track here? – DevSolar Aug 01 '18 at 11:50
  • @DevSolar it might, but this would break `isdigit()` as well, which only relies on the (compile-time) execution character set. At least, that's [what it should do](https://stackoverflow.com/questions/2898228/can-isdigit-legitimately-be-locale-dependent-in-c) (Microsoft begs to differ ...) ;) –  Aug 01 '18 at 11:54
  • @FelixPalmen: I have read the related paragraphs of the standard differently, and still think that the _definition_ of decimal digits is as per 5.2.1, but their binary representation is not... as that would in turn mean that no EBCDIC machine would be able to process ASCII / ISO 8859 input correctly either. Ah well. Perhaps better done in a separate question. ;-) – DevSolar Aug 01 '18 at 11:59
  • @DevSolar I guess that's indeed the case (but very irrelevant in practice). [5.2.1](https://port70.net/~nsz/c/c11/n1570.html#5.2.1) states that the *basic* character set doesn't depend on locale, consequently a footnote to [7.11.1.1 p2](https://port70.net/~nsz/c/c11/n1570.html#7.11.1.1p2) explains "*The only functions in 7.4 whose behavior is not affected by the current locale are isdigit and isxdigit.*". Of course, that's the **C** standard, I didn't have a look at **C++**. –  Aug 01 '18 at 12:15

5 Answers5

2

You can store the values in a container and utilize the std::find_if, std::none_of or std::any_of functions:

#include <iostream>
#include <vector>
#include <algorithm>

int main()
{
    std::vector<char> v = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' };
    char input = '1';
    if (std::none_of(v.cbegin(), v.cend(), [&input](char p){ return p == input; })) {
        std::cout << "None of the elements are equal to input.\n";
    } 
    else {
        std::cout << "Some of the elements are equal to input.\n";
    }
 }
Ron
  • 14,674
  • 4
  • 34
  • 47
1

How do I check if a variable is not equal to multiple things

Is the only option to just do:

input != '1' && input != '2' && input != '3' etc etc

In the general case, for an arbitrary set of values: No, that is not the only option, but it is the simplest. And simplest is often best, or at least good enough.

If you dislike the redundant repetition of input !=, a variadic template can be used to generate the expression. I've written an example of this in another question: https://stackoverflow.com/a/51497146/2079303

In specific cases, there may be better alternatives. There exists std::isdigit for example for exactly the particular case in your example code.

In order to check if a variable is (not) equal to mutliple things which are not known until runtime, the typical solution is to use a set data structure, such as std::unordered_set.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • In this case I was just using digits as an example, the inputs will be words. Thanks for your help though. – Cros Dude Aug 01 '18 at 11:23
  • This isn't what the OP asked for, this isn't a simpler way to do it. – ProXicT Dec 01 '18 at 17:20
  • @ProXicT `This isn't what the OP asked for` what do you think OP asked for then? `this isn't a simpler way to do it.` I answered that OP's suggestion is simplest solution. Obviously what OP suggested isn't simpler than what OP suggested. It is equally simple. – eerorika Dec 02 '18 at 01:50
0

If you are looking for a more general and human-readable construct, you can create something like this:

template <typename T, int TSize>
struct AnyOfThis {    
    template <typename TFirst, typename... TOthers>
    explicit AnyOfThis(TFirst&& first, TOthers&&... others)
        : values({ std::forward<TFirst>(first), std::forward<TOthers>(others)... }) {}

    std::array<T, TSize> values;
};

template <typename TFirst, typename... TOthers>
auto anyOf(TFirst&& first, TOthers&&... others) {
    constexpr std::size_t size = 1 + sizeof...(others);
    return AnyOfThis<typename std::decay<TFirst>::type, size>(std::forward<TFirst>(first),
                                                              std::forward<TOthers>(others)...);
}

template <typename T, int TSize>
bool operator==(const T value, const AnyOfThis<typename std::decay<T>::type, TSize>& anyOfThis) {
    return std::find(anyOfThis.values.begin(), anyOfThis.values.end(), value) != anyOfThis.values.end();
}

Basically, it creates a static array from a variadic function. Then there is another function which serves as a comparator, which takes the value you want to compare and looks for this value in the array.

The use-case reads fairly well, too:

if (1 == anyOf(1, 2, 3)) {
    // do stuff
}

LIVE DEMO AT COLIRU

ProXicT
  • 1,903
  • 3
  • 22
  • 46
0

simple and efficient way would be.

std::unordered_set<char> allowedValues = {'1','2','3','4','5','6','7','8','9','0'};
std::unordered_set<char>::const_iterator index = allowedValues.find(input);
if(index == allowedValues.end())
  std::cout << "Error";
else
  std::cout << "Valid";

by using unordered set you expect O(1) complexity for lookup. It is good when input number is high. If your index is equal to end of set it does not exist in the list, you will get end of set as index which is invalid input for you. otherwise you will count it as a valid input

necilAlbayrak
  • 824
  • 6
  • 9
-1

If you are looking for "if a string is not equal to multiple strings in C" you may use the following (Not everyone would consider it elegant, but if you are fond of good old c-str then you may find it nice. Surely, it is simple and fast):

int GetIdxOfStringInOptionList (const char *Xi_pStr)
{
    char l_P2[205];
    sprintf(l_P2, "<@%s^>", Xi_pStr);  // TODO: if (strlen>=200) return -1. Note that 200 is above length of options string below
    _strlwr(l_P2);                     // iff you want comparison to be case insensitive
    const char *l_pCO = strstr("01<@gps^>02<@gps2^>03<@log^>04<@img^>05<@nogps^>06<@nogps2^>07<@gps3^>08<@pillars0^>09<@pillars1^>10<@pillars2^>11<@pillars3^>", l_P2);
    return l_pCO? atoi(l_pCO-2) : -1;
}