Question

C++: How do i check if a character is between a given range of characters?

Say, if I have a string name. I want to check if the first character of this string is between 'a' to 'n'.

How do I do it?

To do (name[0] == 'a') (name[0] == 'b')... would be too long...

If possible, I would like a solution that deals with ASCII values elegantly.

Was it helpful?

Solution

If you want to check whether or not the first character of you string is between 'a' and 'n', for instance, checking name[0] >= 'a' && name[0] <= 'n' should do the job properly.

Keep in mind, however, that if you can also have caps as a first character in your letter, you have to check (name[0] >= 'a' && name[0] <= 'n') || (name[0] >= 'A' && name[0] <= 'N') instead.

OTHER TIPS

You can use std::all_of in combination with a lambda expression:

std::all_of(name.begin(), name.end(), [](char i) { return (i >= 'a' && i <= 'z'); });

Live demo

This is portable enough for most application, since the character set is usually implemented following the ASCII conventions as explain in §2.3/14:

The glyphs for the members of the basic source character set are intended to identify characters from the subset of ISO/IEC 10646 which corresponds to the ASCII character set. However, because the mapping from source file characters to the source character set (described in translation phase 1) is specified as implementation-defined, an implementation is required to document how the basic source characters are represented in source files.

The complexity of the above algorithm is O(n). The alternative (check every character to be one in the character range with k characters) is O(n*k), but at least you can be sure it's not implementation defined.

If you're sure the used character set on your platform(s) is ASCII, you can use something like :

if (std::all_of(name.begin(), name.end(), [](char c){return ((c >= 'a') && (c <= 'n'));}) ) {
    // name contains only characters between 'a' and 'n' inclusive
}

Otherwise, something like this should do the trick :

if (name.find_first_not_of("abcdefghijklmn") == std::string::npos) {
    // name contains only characters between 'a' and 'n' inclusive
}

An old fashioned portable method:

    bool is_in_range(char range_start, char range_end, char c)
    {
      static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
      unsigned int start_position = 0;
      unsigned int end_position = 0;
      unsigned int character_position = 0;
      c = std::tolower(c);
      for (unsigned int i = 0; i < sizeof(alphabet); ++i)
      {
         if (range_start == alphabet[i])
         {
            start_position = i;
         }
         if (range_end == alphabet[i])
         {
            end_position = i;
         }
         if (c == alphabet[i])
         {
            character_position = i;
         }
      }
      bool result = false;
      if (end_position <= start_position)
      {
        result = false;
      }
      else
      {
        if ((character_position >= start_position) && (character_position <= end_position))
        {
          result = true;
        }
      }
      return result;
}

loop through the string, check every character and see if it stays between a and n using str[i]>'a' and str[i]<'n'

For a contiguous range of characters you can:

_Bool isbetween(int c, int start, int end){
  return ((unsigned)c-start < (end-start));
}

To account for case, use tolower() and the lower case range:

static inline int tolower(int c){
  return c | ( ((unsigned)c-'A' < 26)<<5 );
}
//isbetween(tolower(x),'a','n');

For a non-contiguous range, you may need to create a mask. In this example, I will check for vowels (for brevity because there are only 5, but any combination in a range of 32 could be used or 64 with some modifications ... in fact, a 64 bit mask on a 64 bit platform would eliminate the need for case handling).

static const unsigned vowel_mask =  (1<<('a'-'a'))
  |(1<<('e'-'a'))|(1<<('i'-'a'))|(1<<('o'-'a'))|(1<<('u'-'a'));

int isvowel(int c){ //checks if c is a,A,e,E,i,I,o,O,u,U 
  unsigned x = (c|32)-'a';
  return ((x<32)<<x)&vowel_mask;
}

Note that these implementations contain no branches; however the use of unsigned comparison may prevent automatic compiler vectorization (intel intrinsics, don't have unsigned compare) ... if that is your goal, you can use 2 &ed comparisons instead. This method may or may not work on non-ascii systems depending on the separation distance of the characters.

GCC

isvowel:
        or      edi, 32     # tmp95,
        xor     eax, eax  # tmp97
        sub     edi, 97   # x,
        cmp     edi, 31   # x,
        setbe   al    #, tmp97
        shlx    eax, eax, edi   # tmp99, tmp97, x
        and     eax, 1065233      # tmp96,
        ret

Clang

isvowel: # @isvowel
  or edi, 32
  add edi, -97
  mov eax, 32
  xor ecx, ecx
  cmp edi, eax
  setb cl
  shlx eax, ecx, edi
  and eax, 1065233
  ret

ICC

isvowel:
  xor eax, eax #15.26
  or edi, 32 #14.23
  add edi, -97 #14.27
  cmp edi, 32 #15.26
  setb al #15.26
  shlx eax, eax, edi #15.23
  and eax, 1065233 #15.26
  ret #15.26

In addition to the standard stackoverflow license, this code is released to the Public Domain

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top