3 NATO output

Count yourself blessed if you’ve never had to spell your name over the phone. Or perhaps you’re named Mary Smith, but you live on a street or in a city you must constantly spell aloud. If so, you resort to your own spelling alphabet, something like, “N, as in Nancy” or “K, as in knife.” As a programmer, you can ease this frustration by reading this chapter, where you

  • Understand the NATO phonetic alphabet and why they even bother.

  • Translate words into the spelling alphabet.

  • Read a file to translate words into the phonetic alphabet.

  • Go backward and translate the NATO alphabet into words.

  • Read a file to translate the NATO alphabet.

  • Learn that natto in Japanese is a delicious, fermented soybean paste.

The last bullet point isn’t covered in this chapter. I just enjoy eating natto, and now I can write it off as a business expense.

Anyway.

The glorious conclusion to all this mayhem is to not only learn some new programming tricks but also proudly spell words aloud by saying “November” instead of “Nancy.”

3.1 The NATO alphabet

Beyond being a handy nickname for anyone named Nathaniel, NATO stands for the North Atlantic Treaty Organization. It’s a group of countries who are members of a mutual defense pact.

Established after World War II, blah-blah-blah. I could wax on, but the point is that NATO requires some commonality between its member states. You know, so that when Hans is short on ammo, Pierre can offer him bullets and they fit into the gun. Stuff like that.

One common item shared between NATO countries is a way to spell things out loud. That way, Hans doesn’t need to say, “Bullets! That’s B, as in bratwurst; U, as in über; L, as in lederhosen. . . .” And so on. Instead, Hans says, “Bravo, Uniform, Lima, Lima, Echo, Tango.” This way, Pierre can understand Hans, even over all the surrounding gunfire.

Table 3.1 lists the NATO phonetic alphabet, describing each letter with its corresponding word. The words are chosen to be unique and not easily misunderstood. Two of the words (Alfa and Juliett) are misspelled on purpose to avoid being confusing—and to be confusing.

Table 3.1 The NATO phonetic alphabet.

Letter

NATO

Letter

NATO

A

Alfa

N

November

B

Bravo

O

Oscar

C

Charlie

P

Papa

D

Delta

Q

Quebec

E

Echo

R

Romeo

F

Foxtrot

S

Sierra

G

Golf

T

Tango

H

Hotel

U

Uniform

I

India

V

Victor

J

Juliett

W

Whiskey

K

Kilo

X

Xray

L

Lima

Y

Yankee

M

Mike

Z

Zulu

NATO isn’t the only phonetic alphabet, but it’s perhaps the most common. The point is consistency. As programmer, you don’t need to memorize any of these words, though as a nerd, you probably will. Still, it’s the program that can output NATO code—or translate it back into words, depending on how you write your C code. Oscar Kilo.

3.2 The NATO translator program

Any NATO translator program you write must have a string array, like the one shown here:

const char *nato[] = {
    "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
    "Golf", "Hotel", "India", "Juliett", "Kilo", "Lima",
    "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo",
    "Sierra", "Tango", "Uniform", "Victor", "Whiskey",
    "Xray", "Yankee", "Zulu"
};

The array’s notation, *nato[], implies an array of pointers, which is how the compiler builds this construction in memory. The array’s data type is char, so the pointers reference character arrays—strings—stored in memory. It’s classified as a constant because it’s unwise to create an array of strings as pointers and then risk modifying them later. The nato[] array is filled with the memory locations of the strings, as illustrated in figure 3.1.

03-01

Figure 3.1 How an array of pointers references strings as they sit in memory

For example, in the figure, the string Alfa (terminated with a null character, ) is stored at address 0x404020. This memory location is stored in the nato[] array, not the string itself. Yes, the string appears in the array’s declaration, but it’s stored elsewhere in memory at runtime. The same structure holds true for all elements in the array: each one corresponds to a string’s memory location, from Alfa to Zulu.

The beauty of the nato[] array is that the contents are sequential, matching up to ASCII values 'A' through 'Z' when you subtract the value of 'A'. (See chapter 4 for more details on how this operation works.) This coincidence makes extracting the character corresponding to the NATO word embarrassingly easy.

3.2.1 Writing the NATO translator

A simple NATO translator is shown in listing 3.1. It prompts for input, using the fgets() function to gather a word from standard input. A while loop churns through the word letter by letter. Along the way, any alphabetic characters are detected by the isalpha() function. If found, the letter is used as a reference into the nato[] array. The result is the NATO phonetic alphabet term output.

Listing 3.1 Source code for nato01.c

#include <stdio.h>
#include <ctype.h>
 
int main()
{
    const char *nato[] = {
        "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
        "Golf", "Hotel", "India", "Juliett", "Kilo", "Lima",
        "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo",
        "Sierra", "Tango", "Uniform", "Victor", "Whiskey",
        "Xray", "Yankee", "Zulu"
    };
    char phrase[64];
    char ch;
    int i;
 
    printf("Enter a word or phrase: ");
    fgets(phrase,64,stdin);                
 
    i = 0;
    while(phrase[i])                       
    {
        ch = toupper(phrase[i]);           
        if(isalpha(ch))                    
            printf("%s ",nato[ch-'A']);    
        i++;
        if( i==64 )                        
            break;
    }
    putchar('
');
 
    return(0);
}

Stores into location phrase 63 characters (plus the null character) from stdin, standard input

Loops until the null character is found in the string

Converts ch to uppercase

True when character ch is alphabetic

ch-'A' transforms the letters to values 0 through 25, matching the corresponding array element.

A long string may not have a null character, so bail when the buffer size is reached.

When built and run, the program prompts for input. Whatever text is typed (up to 63 characters) is translated and output in the phonetic alphabet. For example, “Howdy” becomes:

Hotel Oscar Whiskey Delta Yankee

Typing a longer phrase such as “Hello, World!” yields:

Hotel Echo Lima Lima Oscar Whiskey Oscar Romeo Lima Delta

Because nonalpha characters are ignored in the code, no output for them is generated.

Translation into another phonetic alphabet is easy with this code. All you do is replace the nato[] array with your own phonetic alphabet. For example, here is the array you can use for the law enforcement phonetic alphabet:

const char *fuzz[] = {
    "Adam", "Boy", "Charles", "David", "Edward", "Frank",
    "George", "Henry", "Ida", "John", "King", "Lincoln",
    "Mary", "Nora", "Ocean", "Paul", "Queen", "Robert",
    "Sam", "Tom", "Union", "Victor", "William",
    "X-ray", "Young", "Zebra"
};

3.2.2 Reading and converting a file

I’m unsure of the need to translate all the text from a file into the NATO phonetic alphabet. It’s a C project you can undertake, primarily for practice, but practically speaking, it makes little sense. I mean, it would be tedious to hear three hours of Antony and Cleopatra done entirely in the NATO alphabet, though if you’re a theater/IT dual major, give it a shot. Still, this is a book and I’m a nerd, so the topic will be explored for your betterment.

Listing 3.2 presents code that devours a file and translates each character into its NATO phonetic alphabet counterpart. The filename is supplied at the command prompt. If not, the program bails with an appropriate error message. Otherwise, similar to the code in nato01.c, the code churns though the file one character at a time, spewing out the matching NATO words.

Listing 3.2 Source code for nato02.c

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
 
int main(int argc, char *argv[])
{
    const char *nato[] = {
        "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
        "Golf", "Hotel", "India", "Juliett", "Kilo", "Lima",
        "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo",
        "Sierra", "Tango", "Uniform", "Victor", "Whiskey",
        "Xray", "Yankee", "Zulu"
    };
    FILE *n;
    int ch;
 
    if( argc<2 )                                       
    {
        fprintf(stderr,"Please supply a text file argument
");
        exit(1);
    }
 
    n = fopen(argv[1],"r");                            
    if( n==NULL )
    {
        fprintf(stderr,"Unable to open '%s'
",argv[1]);
        exit(1);
    }
 
    while( (ch=fgetc(n))!=EOF )                        
    {
        if(isalpha(ch))                                
            printf("%s ",nato[toupper(ch)-'A']);       
    }
    putchar('
');
 
    fclose(n);
 
    return(0);
}

If fewer than two arguments are present, the filename option is missing.

Opens the filename supplied at the command prompt, referenced as argv[1]

Reads one character at a time from the file, storing it in variable ch. The EOF marks the end of the file

Processes only text characters

Uses the uppercase version of the character, minus the value of 'A' to index the nato[] array

Remember to use integer variables when processing text from a file. The EOF flag that marks the end of a file is an int value, not a char value. The while statement in the code is careful to extract a character from the file as well as evaluate the character to determine when the operation is over.

To run the program, type a filename argument after the program name. Text files are preferred. The output appears as a single line of text reflecting the phonetic alphabet words for every dang doodle character in the file.

For extra fun on the Macintosh, pipe the program’s output through the say command:

nato02 antony_and_cleopatra.txt | say

This way, the phonetic alphabet contents of the file given are read aloud by the Mac, from start to end. Sit back and enjoy.

3.3 From NATO to English

Phonetic alphabet translation is supposed to happen in your head. Someone spells their hometown: India, Sierra, Sierra, Alfa, Quebec, Uniform, Alfa, Hotel. And the listener knows how to write down the word, spelling it properly. The word is Issaquah, which is a city where I once lived. I had to spell the name frequently. The beauty of this operation is that even a person who doesn’t know the NATO alphabet can understand what’s being spelled, thanks to the initial letter.

More difficult, however, is to write code that scans for phonetic alphabet words and translates them into the proper single characters. This process involves parsing input and examining it word by word to see whether one of the words matches a term found in the lexicon.

3.3.1 Converting NATO input to character output

To determine whether a phonetic alphabet term appears in a chunk of text, you must parse the text. The string is separated into word chunks. Only after you pull out the words can you compare them with the phonetic alphabet terms.

To do the heavy lifting, use the strtok() function to parse words in a stream of text. I assume the function name translates as “string tokenizer” or “string to kilograms,” which makes no sense.

The strtok() function parses a string into chunks based on one or more separator characters. Defined in the string.h header file, the man page format is:

char *strtok(char *str, const char *delim);

The first argument, str, is the string to scan. The second argument, delim, is a string containing the individual characters that can separate, or delimit, the character chunks you want to parse. The value returned is a char pointer referencing the character chunk found. For example:

match = strtok(string," ");

This statement scans characters held in buffer string, stopping when the space character is encountered. Yes, the second argument is a full string, even when only a single character is required. The char pointer match holds the address of the word (or text chunk) found, terminated with a null character where the space or another delimiter would otherwise be. The NULL constant is returned when nothing is found.

To continue scanning the same string, the first argument is replaced with the NULL constant:

match = strtok(NULL," ");

The NULL argument informs the function to use the string passed earlier and continue the tokenizing operation. The code shown in the next listing illustrates how to put the strtok() function to work.

Listing 3.3 Source code for word_parse01.c

#include <stdio.h>
#include <string.h>
 
int main()
{
    char sometext[64];
    char *match;
 
    printf("Type some text: ");
    fgets(sometext,64,stdin);
 
    match = strtok(sometext," ");     
    while(match)                      
    {
        printf("%s
",match);
        match = strtok(NULL," ");     
    }
 
    return(0);
}

The initial call to strtok(), with the string to search.

Loops as long as the return value isn’t NULL.

In the second call to strtok(), NULL is used to keep searching the same string.

In this code, the user is prompted for a string. The strtok() function extracts words from the string, using a single space as the separator. Here’s a sample run:

Type some text: This is some text
This
is
some
text

When separators other than the space appear in the string, they’re included in the character chunk match:

Type some text: Hello, World!
Hello,
World!

To avoid capturing the punctuation characters, you can set this delimiter string:

match = strtok(sometext," ,.!?:;"'");

Here, the second argument lists common punctuation characters, including the double quote character, which must be escaped ("). The result is that the delimited words are truncated, as in:

Type some text: Hello, World!
Hello
World

You may find some trailing blank lines in the program’s output. These extra newline characters are fine for matching text, because the blank lines won’t match anything anyhow.

To create a phonetic alphabet input translator, you modify this code to perform string comparisons with an array of NATO phonetic alphabet terms. The strcmp() function handles this task, but you must consider two factors.

First, strcmp() is case-sensitive. Some C libraries feature a strcasecmp() function that performs case-insensitive comparisons, though this function isn’t part of the C standard. Second, the string length may vary. For example, if you choose not to count the punctuation characters (" ,.!?:;"'") in the strtok() function—or when an unanticipated punctuation character appears—the comparison fails.

Given these two situations, I figure it’s best to concoct a unique string comparison function, one designed specifically to check parsed words for a match with a phonetic alphabet term. This function, isterm(), is shown next.

Listing 3.4 The isterm() function

char isterm(char *term)
{
    const char *nato[] = {
        "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
        "Golf", "Hotel", "India", "Juliett", "Kilo", "Lima",
        "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo",
        "Sierra", "Tango", "Uniform", "Victor", "Whiskey",
        "Xray", "Yankee", "Zulu"
    };
    int x;
    char *n,*t;
 
    for( x=0; x<26; x++)
    {
        n = nato[x];                      
        t = term;                         
        while( *n!='' )                 
        {
            if( (*n|0x20)!=(*t|0x20) )    
                break;                    
            n++;                          
            t++;                          
        }
        if( *n=='' )                    
            return( *nato[x] );           
    }
    return('');
}

Sets pointer n to the current NATO word

Pointer t references the term passed.

Loops until the NATO term ends

Logically converts each letter to uppercase and compares; refer to chapter 5 for more info on this and other ASCII tricks.

For no match, the loop breaks and the next term in nato[] is compared.

Increments through each letter

When pointer n is the null character, the terms have matched.

Returns the first letter of the NATO term

The isterm() function accepts a word as its argument. The return value is a single character if the word matches a NATO phonetic alphabet term; otherwise, the null character is returned.

To create a new NATO translation program, add the isterm() function to your source code file, below any existing code. You must include both the stdio.h and string.h header files. Then add the following main() function to build a new program, nato03.c, as shown here.

Listing 3.5 The main() function from nato03.c

int main()
{
    char phrase[64];
    char *match;
    char ch;
    printf("NATO word or phrase: ");
    fgets(phrase,64,stdin);
 
    match = strtok(phrase," ");
    while(match)
    {
        if( (ch=isterm(match))!='' )
            putchar(ch);
        match = strtok(NULL," ");
    }
    putchar('
');
 
    return(0);
}

The code scans the line input for any matching phonetic alphabet terms. The isterm() function handles the job. The matching character is returned and output. Here’s a sample run:

NATO word or phrase: india tango whiskey oscar romeo kilo sierra
ITWORKS

An input sentence with no matching characters outputs a blank line. Mixed characters are output like this:

NATO word or phrase: Also starring Zulu as Kono
Z

If you want to add in code to translate special characters, such as punctuation characters, you can do so on your own. Keep in mind that the NATO phonetic alphabet lacks terms with punctuation, though if you’re creating your own text-translation program, checking for special characters might be required.

3.3.2 Reading NATO input from a file

Reading input to detect and translate an alphabetic language is silly but a good exercise. Reading an entire file to detect an alphabetic language is even sillier. I try not to think of it as a necessity but rather as programming practice: can you scan a file for specific words and then report on their presence? Adopt this notion to justify completing such a program.

As with reading a line of text, to process text in a file for signs of NATO alphabet words, you need the isterm() function. The file reads a line at a time, and the contents of each line are examined similarly to the code presented in nato03.c. Mixing in the file commands from nato02.c, I’ve created a child program, nato04.c. It’s found in this book’s GitHub repository. Assembling such a program in a kind of Frankenstein way appeals to me. It’s the philosophy upon which Stack Overflow is successful.

The guts of nato04.c process an open file by using two while loops, illustrated in the next listing. If you’ve been following along with the NATO series of programs in this chapter, many of the statements are familiar to you.

Listing 3.6 Processing words in a file with nested loops

while( !feof(n) )                                 
{
    fgets(phrase,64,n);                           
    match = strtok(phrase," ,.!?=()[]{}'"");     
    while(match)                                  
    {
        if( (ch=isterm(match))!='' )            
            putchar(ch);
        match = strtok(NULL," ,.!?=()[]{}'"");
    }
}
putchar('
');

Loops until the end of open file handle n

Grabs a line of text up to 63 characters

Filters out a lot of characters

Loops until all the words in the line are read

Sends the matching word off to the isterm() function

The result of all this cobbled code is to pluck out any matching NATO phonetic alphabet terms stored in a file and pop out the corresponding letter for each. As you may guess, few files have a NATO term smuggled inside, so the output is often empty. Still, I ran the code using the nato04.c source code file as input:

$ nato04 nato04.c
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Much to its delight, the program found the nato[] array’s text and gobbled up all the alphabetic terms, in order, to spew out the alphabet itself. Wonderful.

One problem with the code in nato04.c is that the fgets() function reads in only a slice of characters per line. In the source code, if a line of text in the file is shorter than the given number of characters (63 plus one for the null character), the line of text is read up to and including the newline character. If the line of text in a file is longer than the quantity specified by the fgets() function, it’s truncated. Truncating text when you’re looking for a word is bad, though not as bad as truncating an elephant.

To better process the file, and ensure that words aren’t split by an unforgiving fgets() function, I’ve recajiggered the code to read the file one character at a time. In this approach, the code works more like a program filter. (Filters are covered in chapter 4.) The words in the file are assembled as each character is digested.

Listing 3.7 shows a while loop that processes an open file, represented by FILE handle n. Characters are stored in int variable ch, read one at a time by using the fgetc() function. The integer variable offset tracks the characters read as they’re stored in a word[] buffer. This buffer is 64 characters long. If a buffer overflow occurs, the program terminates. I mean, show me a word longer than 64 characters. And if you can legitimately find one, increase the buffer size.

Listing 3.7 Processing words in a file one at a time

offset = 0;
while( (ch=fgetc(n))!=EOF )             
{
    if( isalpha(ch)  )                  
    {
        word[offset] = ch;              
        offset++;
        if( offset>=64 )                
        {
            fprintf(stderr,"Buffer overflow
");
            return(1);
        }
    }
    else                                
    {
        if( offset > 0 )                
        {
            word[offset] = '';        
            putchar( isterm(word) );    
            offset=0;                   
        }
    }
}
putchar('
');

Loops as long as the file has bytes to read

Words start with a letter of the alphabet.

Stores the character to build the word

Checks for overflow; bails if so

A nonalphabetic character is found, meaning the end of a word.

Confirms that the word[] buffer has some text in it

Cap your strings!

Processes the word, returning a valid character or the null character (doesn’t print)

Resets the offset to store the next word

The code shown in listing 3.7 is part of the nato05.c source code file, available in this book’s GitHub repository. The program works similarly to nato04.c, though a long line of text read from the file isn’t split—which could split a valid word. By processing the file’s text one character at a time, such a split can’t happen (unless the word is ridiculously long).

The program’s output is identical to that of nato04.c in the case of processing the source code file’s text:

$ nato05 nato05.c
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Like any program, the code for nato05.c can be improved upon. As it’s written, the code relies upon a nonalphabetic character to terminate a word: the isalpha() function returns TRUE when the character (int value) examined is in the range 'A' to 'Z' or 'a' to 'z'. This rule eliminates contractions (don’t, o’clock), though it’s rare such contractions would be included in a phonetic alphabet.

Beyond peeking into a file for NATO phonetic alphabetic terms, the code provides a practical example of how to scan any file for specific words. Consider it inspiration for other programs you may create. Or just enjoy your newfound knowledge of the NATO phonetic alphabet, so you can beam with pride when asked to spell your city name over the phone.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset