468,283 Members | 2,044 Online

# Test for contiguous alphabet in character set

Hello,

I am currently writing code where it is convenient to convert
char [a-zA-Z] to int [0-25]. The conversion function relies on
a character set with contiguous alphabets.

int set_mesg(Key *key, char *s)
{
char *x;

if (strlen(s) != 3)
return 0;

x = s;
while (*x != '\0') {
if (!isalpha(*x))
return 0;
*x = tolower(*x);
x++;
}

x = s;
/* l_mesg, m_mesg, r_mesg are int */
key->l_mesg = *x++ - 'a';
key->m_mesg = *x++ - 'a';
key->r_mesg = *x - 'a';

return 1;
}
According to K&R2 (p43) contiguous alphabets cannot be safely assumed.
This function would test the lowercase alphabet:

int cont_lower_alpha(void)
{
char a[26] = "abcdefghijklmnopqrstuvwxyz";
int i;

for (i = 0; i < 26; i++)
if (a[i] - 'a' != i)
return 0;

return 1;
}

Is there an easier way of doing this?
Stefan Krah
Nov 14 '05 #1
5 6202

Stefan Krah wrote:
Hello,

I am currently writing code where it is convenient to convert
char [a-zA-Z] to int [0-25]. The conversion function relies on
a character set with contiguous alphabets.

int set_mesg(Key *key, char *s)
{
char *x;

if (strlen(s) != 3)
return 0;

x = s;
while (*x != '\0') {
if (!isalpha(*x))
FYI: Use `isalpha((unsigned char)*x)', and similarly
for the other <ctype.h> functions.
return 0;
*x = tolower(*x);
x++;
}

x = s;
/* l_mesg, m_mesg, r_mesg are int */
key->l_mesg = *x++ - 'a';
key->m_mesg = *x++ - 'a';
key->r_mesg = *x - 'a';

return 1;
}

According to K&R2 (p43) contiguous alphabets cannot be safely assumed.
This function would test the lowercase alphabet:

int cont_lower_alpha(void)
{
char a[26] = "abcdefghijklmnopqrstuvwxyz";
int i;

for (i = 0; i < 26; i++)
if (a[i] - 'a' != i)
return 0;

return 1;
}

Is there an easier way of doing this?

This tests contiguity of the lower-case alphabet, and
upper-case could be tested in the same way. But if the
test says "discontiguous," then what? Your real problem,
I think, is not to determine whether the alphabets are
contiguous, but to find some code that will work correctly
even if they are not contiguous.

One way would be to use the position of the character
in a reference string instead of the character's code. For
example, you could write

const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
...
key->l_mesg = strchr(alphabet, *x++) - alphabet;

As written, this is unsafe if there's any chance that
the target character might not be found in alphabet[], because
strchr() would then return NULL and `NULL - alphabet' is
nonsense. (You may think this cannot happen since the code
has eliminated all non-alphabetics and converted everything
to lower-case, but keep in mind that isalpha() and tolower()
are locale-dependent. Letters like å, ç, ñ, and þ are found
in many character sets, and may be considered lower-case
alphabetics in some locales -- so they would pass through the
earlier portions of your code only to be found missing from
the alphabet[] array.) You could call strchr() and check the
result for NULL before trying to subtract, or you could make
sure that strchr() always finds the target character:

char alphabetplus[] = "abcdefghijklmnopqrstuvwxyz?";
int pos;
...
alphabetplus[26] = *x;
pos = strchr(alphabetplus, *x++) - alphabetplus;
if (pos < 26)
key->l_mesg = pos;
else
return 0; /* unknown lower-case alphabetic */

If you intend to compute a large number of these message
codes, though, it is probably better to use a table:

static char code[1+UCHAR_MAX];
if (code['a'] == 0) {
/* initialize table on the first call */
const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
int i;
for (i = 0; alpha[i] != '\0'; ++i) {
code[alpha[i]] = i + 1;
code[toupper(alpha[i])] = i + 1;
}
}
...
if (code[(unsigned char)*x] == 0)
return 0;
key->l_mesg = code[(unsigned char)*x] - 1;

(Note 1: Yes, I warned you to cast the argument of <ctype.h>
functions, yet I did not do so in the toupper() call. This
happens to be safe because I know that all the characters in
a..z are in the "basic execution" character set, and all these
are guaranteed to have non-negative code values. When you don't
have such knowledge of the input string, though, you must cast --
as in the references to the code[] array, although only the first
of those two is strictly necessary.)

(Note 2: Observe that the table-based method eliminates
the need to weed out non-alphabetics and convert case. All
letters outside a..z and A..Z will be detected by virtue of
their zero code[] values, and for all the rest you will have
code['a'] == code['A'], code['b'] == code['B'], and so on.)

--
Er*********@sun.com
Nov 14 '05 #2

"Stefan Krah" <sf**@bigfoot.com> wrote
int cont_lower_alpha(void)
{
char a[26] = "abcdefghijklmnopqrstuvwxyz";
int i;

for (i = 0; i < 26; i++)
if (a[i] - 'a' != i)
return 0;

return 1;
}

Is there an easier way of doing this?

You could do conditional compilation with
#if 'z' == 'a' + 25

strictly it isn't foolproof, because a perverse implementer could make the
intervening letters non-contiguous. But there are only a few character sets
out there, and it is probably much more likely that you program will break
in some other way than that someone will bring out a character set that
Nov 14 '05 #3
* Eric Sosman <er*********@sun.com> wrote:
[I 'm quoting a lot, it will be needed later]
Stefan Krah wrote:
Hello,

I am currently writing code where it is convenient to convert
char [a-zA-Z] to int [0-25]. The conversion function relies on
a character set with contiguous alphabets.

int set_mesg(Key *key, char *s)
{
char *x;

if (strlen(s) != 3)
return 0;

x = s;
while (*x != '\0') {
if (!isalpha(*x))
FYI: Use `isalpha((unsigned char)*x)', and similarly
for the other <ctype.h> functions.
return 0;
*x = tolower(*x);
x++;
}

x = s;
/* l_mesg, m_mesg, r_mesg are int */
key->l_mesg = *x++ - 'a';
key->m_mesg = *x++ - 'a';
key->r_mesg = *x - 'a';

return 1;
}
According to K&R2 (p43) contiguous alphabets cannot be safely assumed.
This function would test the lowercase alphabet:

int cont_lower_alpha(void)
{
char a[26] = "abcdefghijklmnopqrstuvwxyz";
int i;

for (i = 0; i < 26; i++)
if (a[i] - 'a' != i)
return 0;

return 1;
}

Is there an easier way of doing this?

This tests contiguity of the lower-case alphabet, and
upper-case could be tested in the same way. But if the
test says "discontiguous," then what? Your real problem,
I think, is not to determine whether the alphabets are
contiguous, but to find some code that will work correctly
even if they are not contiguous.

Your observations are absolutely correct. My vague idea was to use
the test as a safety net for the (potentially) rare cases in which
a discontiguous alphabet is encountered.

Obviously it is silly not to write portable code in the first place.

One way would be to use the position of the character
in a reference string instead of the character's code. For
example, you could write

const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
...
key->l_mesg = strchr(alphabet, *x++) - alphabet;

As written, this is unsafe if there's any chance that
the target character might not be found in alphabet[], because
strchr() would then return NULL and `NULL - alphabet' is
nonsense. (You may think this cannot happen since the code
has eliminated all non-alphabetics and converted everything
to lower-case, but keep in mind that isalpha() and tolower()
are locale-dependent. Letters like å, ç, ñ, and þ are found
in many character sets, and may be considered lower-case
alphabetics in some locales -- so they would pass through the
earlier portions of your code only to be found missing from
the alphabet[] array.) You could call strchr() and check the
I was aware of the locale issue, but I thought the default locale
is "C" unless explicitly set:

SETLOCALE(3) (Linux Programmer's Manual):
| On startup of the main program, the portable "C" locale
| is selected as default.

C Standard Draft -- August 3, 1998:
| At program startup, the equivalent of
| setlocale(LC_ALL, "C");
| is executed.

isalpha() is equivalent to (isupper(c) || islower(c)) and those
two (in the C locale) only return true for [a-zA-Z].

So isalpha() shouldn't return false positives or am I missing something?

If you intend to compute a large number of these message
codes, though, it is probably better to use a table:

static char code[1+UCHAR_MAX];
if (code['a'] == 0) {
/* initialize table on the first call */
const char alpha[] = "abcdefghijklmnopqrstuvwxyz";
int i;
for (i = 0; alpha[i] != '\0'; ++i) {
code[alpha[i]] = i + 1;
code[toupper(alpha[i])] = i + 1;
}
}
...
if (code[(unsigned char)*x] == 0)
return 0;
key->l_mesg = code[(unsigned char)*x] - 1;
I like this one even though conversion doesn't have to be fast in my case.

(Note 1: Yes, I warned you to cast the argument of <ctype.h>
functions, yet I did not do so in the toupper() call. This
happens to be safe because I know that all the characters in
a..z are in the "basic execution" character set, and all these
are guaranteed to have non-negative code values. When you don't
have such knowledge of the input string, though, you must cast --
as in the references to the code[] array, although only the first
of those two is strictly necessary.)

(Note 2: Observe that the table-based method eliminates
the need to weed out non-alphabetics and convert case. All
letters outside a..z and A..Z will be detected by virtue of
their zero code[] values, and for all the rest you will have
code['a'] == code['A'], code['b'] == code['B'], and so on.)

Stefan Krah
Nov 14 '05 #4
Stefan Krah <sf**@bigfoot.com> wrote:
I was aware of the locale issue, but I thought the default locale
is "C" unless explicitly set:

Well, until you use some library that changes it behind your back.
And, of course, that never happens on your own machine, but always
on a machine some 1000 km away. This resulted in a _lot_ of head
scratching until I figured out what was going on.... It's astoni-
shing in which places a wrongly set locale can mess things up;-)

Regards, Jens
--
\ Jens Thoms Toerring ___ Je***********@physik.fu-berlin.de
\__________________________ http://www.toerring.de
Nov 14 '05 #5
Stefan Krah <sf**@bigfoot.com> wrote:
# Hello,
#
# I am currently writing code where it is convenient to convert
# char [a-zA-Z] to int [0-25]. The conversion function relies on
# a character set with contiguous alphabets.

#include <limits.h>

char map0[CHAR_MAX-CHAR_MIN+1]; memset(map0,26,sizeof map0);
char map = map0-CHAR_MIN;

int i; for (i=0; i<=25; i++) map[i["abcdefghijklmnopqrstuvwxyz"]] = i;
Anywhere you want to map letters to the integers, use map[lettercharacter].
Whether the letters are contiguous or positive or negative can be ignored.

--
SM Ryan http://www.rawbw.com/~wyrmwif/
So basically, you just trace.
Nov 14 '05 #6

### This discussion thread is closed

Replies have been disabled for this discussion.