How to delete characters except alphabets in c

Viewed 70

There is a file "poem.txt":

*The ho$use cat sits.* 

*And sm%iles and) sing&s.*

*He% know*(s a l_ot* 

*Of s!ecret thi<ngs.*

I need to delete unnecessary symbols from it and write it to another file "poem_modified" without using arrays, functions, structures and pointer and only with <stdio.h> library:

I was able to do it so far:

#include <stdio.h>

int main() {
FILE *input;
FILE *output;

input  = fopen ("poem.txt", "r");
output = fopen ("poem_modified.txt", "w");

if (input == NULL || output == NULL)
{
    printf("Problem! \n");
    return 1;
}
char ch ;
while((ch=getc(input)) != EOF)

fprintf(output, "%c", ch);

fclose(input);
fclose(output); 
}
3 Answers

Adding conditions while printing the character can help Suppose, it is required to include a-z and A-X only with spaces and newline char. So conditions can be made such as if the character is between a-z or between A-Z or it is newline or space, the char will be printed. Otherwise not. Any other conditions can be added.

The getc() function return type is an integer. documentation

Correct indentation helps to understand the code.

#include <stdio.h>

int main() {
    FILE *input;
    FILE *output;

    input  = fopen ("poem.txt", "r");
    output = fopen ("poem_modified.txt", "w");

    if (input == NULL || output == NULL)
    {
        printf("Problem! \n");
        return 1;
    }
    int ch ;

    while((ch=getc(input)) != EOF) {
        if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == ' ' || ch == '\n'){
            fprintf(output, "%c", ch);
        }
    }

    fclose(input);
    fclose(output);
}

output :

The house cat sits

And smiles and sings

He knows a lot

Of secret things

Portability Concerns

Using the > and < operators on characters is not a portable solution for this in C. For the special case of the digits '0'...'9' you can do this because the C Standard specifies that these characters must be encoded contiguously and in ascending order.

It is unlikely that using comparison operators to check whether a character is alphabetic in the manner (ch >= 'A' && ch <= 'Z') will cause problems on most modern systems, but problems do occur. Certainly it could be a problem on older systems, such as legacy systems installed at institutions many years ago. This is exactly why the functions described in ctype.h should usually be preferred: these can be relied upon to work portably.

Portable Solutions

But if this is not possible more portable solutions than the aforementioned char comparison which relies upon a particular character encoding can be had.

Being unable to use arrays is a severe (and artificial) constraint. Of the two solutions below, the first solution does use an array (keepers) to encode characters which should be written to output. There is another solution following which does not use such an array, and I think that it meets all of OP's requirements, yet the second solution is a bit more awkward and error-prone to write.

Both solutions are more portable than using (ch >= 'A' && ch <= 'Z') methods, and both give the same results:

$ cat poem_modified.txt 
The house cat sits 

And smiles and sings

He knows a lot 

Of secret things

Using an Array

The first solution defines an array keepers which is initialized by a string literal to contain all characters which should be written to output. As characters are read from input, the program checks in keepers to see if the character is present in this list; if so it is written to output, and if not the next character is read from input.

#include <stdio.h>

int main(void) {
    // Open input file and check for errors
    const char *input_file = "poem.txt";
    FILE *input = fopen(input_file, "r");
    if (input == NULL) {
            fprintf(stderr, "Unable to open file %s for input\n", input_file);
            return 1;
        }

    // Open output file and check for errors
    const char *output_file = "poem_modified.txt";
    FILE *output = fopen(output_file, "w");
    if (output == NULL) {
            fprintf(stderr, "Unable to open file %s for output\n", output_file);
            fclose(input);
            return 1;
        }

    char keepers[] =
        "abcdefghijklmnopqrstuvwxyz"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ \n";

    for (int ch = fgetc(input); ch != EOF; ch = fgetc(input)) {
        // Is `ch` an alphabetic character?
        size_t idx = 0;
        char keeper = keepers[idx];
        while(keeper != '\0') {
            if (ch == keeper) {
                putc(ch, output);
                break;
            }
            keeper = keepers[++idx];
        }
    }

    fclose(input);
    fclose(output);

    return 0;
}

Using Brute Force

The second solution does the same thing as the first, but without the array. Here instead of using an array to hold the list of characters which should be kept, an if statement with a very long conditional expression encodes this information.

#include <stdio.h>

int main(void) {
    // Open input file and check for errors
    const char *input_file = "poem.txt";
    FILE *input = fopen(input_file, "r");
    if (input == NULL) {
            fprintf(stderr, "Unable to open file %s for input\n", input_file);
            return 1;
        }

    // Open output file and check for errors
    const char *output_file = "poem_modified.txt";
    FILE *output = fopen(output_file, "w");
    if (output == NULL) {
            fprintf(stderr, "Unable to open file %s for output\n", output_file);
            fclose(input);
            return 1;
        }

    for (int ch = fgetc(input); ch != EOF; ch = fgetc(input)) {
        // Is `ch` an alphabetic character, space, or newline?
        if (ch == 'a' || ch == 'b' || ch == 'c' || ch == 'd' || ch == 'e'
            || ch == 'f' || ch == 'g' || ch == 'h' || ch == 'i' || ch == 'j'
            || ch == 'k' || ch == 'l' || ch == 'm' || ch == 'n' || ch == 'o'
            || ch == 'p' || ch == 'q' || ch == 'r' || ch == 's' || ch == 't'
            || ch == 'u' || ch == 'v' || ch == 'w' || ch == 'x' || ch == 'y'
            || ch == 'z' || ch == 'A' || ch == 'B' || ch == 'C' || ch == 'D'
            || ch == 'E' || ch == 'F' || ch == 'G' || ch == 'H' || ch == 'I'
            || ch == 'J' || ch == 'K' || ch == 'L' || ch == 'M' || ch == 'N'
            || ch == 'O' || ch == 'P' || ch == 'Q' || ch == 'R' || ch == 'S'
            || ch == 'T' || ch == 'U' || ch == 'V' || ch == 'W' || ch == 'X'
            || ch == 'Y' || ch == 'Z' || ch == ' ' || ch == '\n')
        {
            putc(ch, output);
        }
    }

    fclose(input);
    fclose(output);

    return 0;
}

You can use the various functions from ctype.h to check if something belongs to a certain category of symbols. For example isalpha checks if a character is a letter and isspace checks if it's a space or new line character etc. By using these two functions in combination, we can chose to only print characters that are either letters or spaces. Example:

#include <stdio.h>
#include <ctype.h>
#include <string.h>

int main (void)
{
  char input[] = "*The ho$use cat sits.*\n"
                 "*And sm%iles and) sing&s.*\n"
                 "*He% know*(s a l_ot*\n"
                 "*Of s!ecret thi<ngs.*\n";

  size_t length = strlen(input);
  for(size_t i=0; i<length; i++)
  {
    if(isalpha(input[i]) || isspace(input[i]))
    {
      putchar(input[i]);
    }
  }
}

Apart from the ctype.h functions making the code easier to read, manual checks like ch >= 'A' && ch <= 'Z' are strictly speaking not well-defined or portable. Because C doesn't guarantee that letters are placed adjacently in the symbol table (see for example the EBCDIC, which was a format used in the Jurassic era). Also the ctype.h functions might handle "locale-specific" characters outside the classic 7 bit ASCII.

Related