C: Program to delete characters other than alphabetical - c

so I'm trying to create a program which looks at a string defined in main, and deletes any non-alphabetical characters (excluding \0). So far this is my code:
/* Write code to which considers the string currently saved
* in the 'name' array, removes all spaces and non-alphabetical
* chars from the string, and makes all alphabetical characters
* lower case. */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define NAMELEN 30
int main (void) {
char name[NAMELEN];
strcpy(name, " William B. Gates");
int i, length, check;
length = strlen(name);
for ( i = 0; i < length; i++ ) {
check = isalpha(name[i]);
if ( check == 0 ) {
for ( ; i < length; i++ ) {
name[i] = name[i+1];
}
}
}
printf("The length is %lu.\n", strlen(name));
printf("Name after compression: %s\n", name);
return EXIT_SUCCESS;
}
So for the test data, " William B. Gates", the output should be "WilliamBGates", unfortunately the output I'm getting is:
The length is 16.
Name after compression: William B. Gates
I think the space before William has been deleted, but I'm unable to tell.
Thanks for any help!

You don't need a complicated double-loop for this at all. The purpose of the exercise is to maintain independent source-reader and destination-writer, only copying and advancing the latter when the former is qualified by your criteria (i.e. it answers true to isalpha).
in other words:
#include <stdio.h>
#include <ctype.h>
int main (void)
{
char name[] = " William B. Gates";
char *dst = name, *src;
for (src = name; *src; ++src)
{
if (isalpha((unsigned char)*src))
*dst++ = *src;
}
*dst = 0; // terminate the string
printf("result: %s\n", name);
}
Output
result: WilliamBGates
I leave translating to lower case during the copy-step as an exercise for you. (from your in-code comment: "makes all alphabetical characters lower case").

This inner loop is wrong
if ( check == 0 ) {
for ( ; i < length; i++ ) {
name[i] = name[i+1];
}
It copies the string in itself excluding the first character and after that your program does nothing because i is already equal to length.
So the program only removes one non-alpha character from the string.
When you are going to traverse a string sequantially then there is no need to calculate its length. The program can be written simpler or at least you may use the approach that is demonstrated below. For example
#include <stdio.h>
#include <ctype.h>
int main( void )
{
char name[] = " William B. Gates";
printf( "\"%s\"\n", name );
size_t j = 0;
for ( size_t i = 0; name[i] != '\0'; i++ )
{
if ( isalpha( ( unsigned char )name[i] ) )
{
if ( j != i ) name[j] = name[i];
++j;
}
}
name[j] = '\0';
printf( "\"%s\"\n", name );
}
The program output is
" William B. Gates"
"WilliamBGates"

The posted code tries to hard.
The following code
compiles cleanly
contains useful comments to clarify what is being done
performs correctly
uses meaningful variable names
notice the simplicity of the code, and all performed in only one pass through the name[] array.
#include <stdio.h>
#include <ctype.h>
int main( void )
{
char name[] = " William B. Gates";
printf( "\"%s\"\n", name );
size_t src = 0;
size_t dest = 0;
for( ; name[src]; src++) // will exit loop when string terminator '\0' encountered
{
if( isalpha(name[src]) )
{ // then current char is: a...zA...Z
name[dest] = tolower(name[src]); // force lower case
dest++; // update where to save next character
}
}
name[dest] = '\0'; // terminate the modified string
printf( "\"%s\"\n", name );
return 0;
} // end function: main

Related

How to use toupper library function in C?

I need to print the initials of a name, like tyler jae woodbury would print TJW, but I can't seem to print the uppercase initials.
This is my code:
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
string get_initials(string name, char initials[]);
int main(void)
{
// User input
string name = get_string("Name: ");
// Gets the users initials
char initials[10];
get_initials(name, initials);
printf("%s\n", initials);
}
string get_initials(string name, char initials[])
{
int counter = 0;
for (int i = 0, n = strlen(name); i < n; i++)
{
if (name[i] == ' ')
{
initials[counter] = name[i+1];
counter++;
}
else if (i == 0)
{
initials[counter] = name[i];
counter++;
}
}
return initials;
}
I know that usually toupper() is used for chars, and the print statement declares a string, but I don't know what to do.
The function is incorrect.
For starters in general a string can contain adjacent spaces between words or have trailing adjacent spaces.
Secondly the function does not build a string because it does not append the terminating zero character '\0' to the destination array.
Also the call of strlen is inefficient and redundant.
To convert a symbol to upper case use standard function toupper declared in the header <ctype.h>
Also the function declaration is confusing
string get_initials(string name, char initials[]);
Either use
string get_initials(string name, string initials);
or it will be better to write
char * get_initials( const char *name, char *initials);
The function can be defined the following way as shown in the demonstration program below.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
char * get_initials( const char *name, char *initials )
{
const char *blank = " \t";
char *p = initials;
while ( name += strspn( name, blank ), *name )
{
*p++ = toupper( ( unsigned char )*name );
name += strcspn( name, blank );
}
*p = '\0';
return initials;
}
int main( void )
{
char name[] = " tyler jae woodbury ";
char initials[10];
puts( get_initials( name, initials ) );
}
The program output is
TJW

Completing a function using pointers

one of the assignments in my class has this objective:
Complete CapVowels(), which takes a string as a parameter and returns a new string containing the string parameter with the first occurrence of each of the five English vowels (a, e, i, o, and u) capitalized.
Hint: Begin CapVowels() by copying the string parameter to a newly allocated string.
Ex: If the input is:
management
the output is:
Original: management
Modified: mAnagEment
This is the current code I have, and I will highlight the section I'm supposed to complete:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
**// Return a newly allocated copy of original
// with the first occurrence of each vowel capitalized
char* CapVowels(char* original) {
return CapVowels(*original= "A.E,I,O,U");
}**
int main(void) {
char userCaption[50];
char* resultStr;
scanf("%s", userCaption);
resultStr = CapVowels(userCaption);
printf("Original: %s\n", userCaption);
printf("Modified: %s\n", resultStr);
// Always free dynamically allocated memory when no longer needed
free(resultStr);
return 0;
}
The section with the ** meaning it's bolded is the section I'm supposed to complete before the int main(void). I can't figure out how to complete the objective. I get mixed up with pointers and dereferencing and, I tried dereferencing when returning the function so that the value will come out to what it's supposed to. I understand one part of it, but I don't know how you would complete it to output to the required output:
Original: management
Modified: mAnagEment
Hint: Begin CapVowels() by copying the string parameter to a newly allocated string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Return a newly allocated copy of original
// with the first occurrence of each vowel capitalized
char* CapVowels(const char* original) {
char* result = strcpy(malloc(strlen(original+1)), original);
char* vowels = "aeiou";
while(*vowels)
{
char* ptr = strchr(result, *vowels);
(ptr)? *ptr = toupper(*ptr) : vowels++;
}
return result;
}
int main(void) {
char userCaption[50];
char* resultStr;
scanf("%s", userCaption);
resultStr = CapVowels(userCaption);
printf("Original: %s\n", userCaption);
printf("Modified: %s\n", resultStr);
// Always free dynamically allocated memory when no longer needed
free(resultStr);
return 0;
}
Output
Success #stdin #stdout 0s 5424KB
Original: management
Modified: mAnAgEmEnt
You can use strlen to get the length of the input, then use malloc to allocate enough space for the result. Then, just loop over the input until the terminating null character ('\0'), incrementally assigning the current character to the result if it is a consonant or the uppercase version if it is a vowel (using the toupper function).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *CapVowels(char *original){
if (!original)
return NULL;
const char *vowels = "aeiou";
size_t len = strlen(original); // get length of input (terminating '\0' not included)
char *result = malloc(len + 1); // allocate memory for new string (note that sizeof(char) is 1)
for (char *dest = result; *original; ++original)
*dest++ = strchr(vowels, *original) // check if current character is in the vowels
? toupper(*original) : *original;
result[len] = '\0';
return result;
}
Here's a version that works even with multiple words in 'original'.
char *CapVowels( const char *original ) {
char *cp, *out = strdup( original );
for( char *vowels = "aeiou"; *vowels; vowels++ ) {
if( ( cp = strchr( out, *vowels ) ) != NULL )
*cp = toupper( *cp );
}
return out;
}
void main( void ) {
char userCaption[50];
gets( userCaption );
char *capped = CapVowels( userCaption );
printf( "Original: %s\n", userCaption );
printf( "Modified: %s\n", capped );
free( capped );
}

Strlwr function - getting an error in xcode 9.2

I'm trying to convert a string from upper case to lower case to check if it is a palindrome, however I keep getting the error:
"function declaration is not a prototype"
I already added #include <string.h> in the header, but it still doesn't work. How do I get around this issue?
This is the code:
int main (void)
{
char *user_string, *user_string_rev;
/* the malloc function is used to make sure that enough memory is allocated for the string and that it does not overwrite memory boxes of other variables. */
user_string= (char*)malloc(BUFF_SIZE*sizeof(char));
user_string_rev= (char*)malloc(BUFF_SIZE*sizeof(char));
printf("Please enter a string:");
fgets(user_string,BUFF_SIZE, stdin); /* fgets function take the string the user inputs and stores it into user_string. */
user_string_rev=strcpy(user_string_rev, user_string); /*the strcpy takes the string the user inputs and copies it to user_string_rev. */
strlwr(user_string_rev);
palindrome_check(user_string,user_string_rev); /*this is the palindrome function used to check if the two strings are palindromes, it intakes two arguments, the two strings and does not return anything. */
return 0;
}
Replace :
strlwr(user_string_rev);
which is not a standard function with:
int i = 0;
while (user_string_rev[i])
{
if (isalpha(user_string_rev[i]))
user_string_rev[i] |= 32;
++i;
}
Don't forget to add the ctype header at the top of your .c file to use isalpha:
#include <ctype.h>
the following proposed code:
incorporates the comments to the question
cleanly compiles
properly checks for errors
will treat a string that is nothing but a newline as NOT a palindrome
And now the proposed code:
#include <stdio.h> // getline(), printf()
#include <stdlib.h> // free()
#include <ctype.h> // tolower()
#include <string.h> // strlen(), strchr()
// prototypes
void palindrome( char *, size_t length );
int main( void )
{
char *inputStr = NULL;
size_t lengthStr = 0;
printf("Please enter a string:");
if( -1 != getline( &inputStr, &lengthStr, stdin ) )
{
size_t length = strlen( inputStr );
for( size_t i = 0; i < length; i++ )
{
inputStr[i] = (char)tolower( inputStr[i] );
}
char *newline = strchr( inputStr, '\n' );
if( newline )
{
*newline = '\0';
length--;
}
palindrome( inputStr, length );
}
free( inputStr );
return 0;
}
void palindrome( char stringToCheck[], size_t length )
{
size_t index = length - 1; // don't check NUL terminator byte
size_t i;
for( i = 0; i < index; i++ )
{
if( stringToCheck[i] != stringToCheck[ index ] )
{
break;
}
index--;
}
if( i < index )
{
printf( "%s is not a palindrome\n", stringToCheck );
}
else
{
printf( "%s is a palindrome\n", stringToCheck );
}
}

Segmentation fault on strcat

I have recently begun working on learning the C language and have repeatedly run into an error in which calling the strcat function from the <string.h> module results in a segmentation fault. I've searched for the answers online, including on this stackoverflow post, without success. I thought this community might have a more personal insight into the problem, as the general solutions don't seem to be working. Might be user error, might be a personal issue with the code. Take a look.
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
char *buffer = malloc(strlen(new)+1);
for (int i=0; i<strlen(*str); i++){
if(buffer!=NULL){
if(str[i]!=" "){
strcat(new,str[i]); //Segmentation fault
}
}
}
free(buffer);
return new;
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
puts(new);
}
I've placed a comment on the line I've traced the segmentation fault back to. The following is some Java to make some sense out of this C code.
public class deblank {
public static void main(String[]args){
String str = "This has space in it.";
System.out.println(removeBlanks(str));
}
public static String removeBlanks(String str){
String updated = "";
for(int i=0; i<str.length(); i++){
if(str.charAt(i)!=' '){
updated+=str.charAt(i);
}
}
return updated;
}
}
Any insights into this error will be much appreciated. Please point out typos as well... I've been known to make them. Thanks.
OK, let's do this.
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
^ This line creates an array of pointers, not a string.
char *buffer = malloc(strlen(new)+1);
malloc is undeclared. Missing #include <stdlib.h>. Also, you should check for allocation failure here.
strlen(new) is a type error. strlen takes a char * but new is (or rather evaluates to) a char **.
for (int i=0; i<strlen(*str); i++){
strlen(*str) is a type error. strlen takes a char * but *str is a char (i.e. a single character).
i<strlen(...) is questionable. strlen returns size_t (an unsigned type) whereas i is an int (signed, and possibly too small).
Calling strlen in a loop is inefficient because it has to walk the whole string to find the end.
if(buffer!=NULL){
This is a weird place to check for allocation failure. Also, you don't use buffer anywhere, so why create/check it at all?
if(str[i]!=" "){
str[i]!=" " is a type error. str[i] is a char whereas " " is (or rather evaluates to) a char *.
strcat(new,str[i]); //Segmentation fault
This is a type error. strcat takes two strings (char *), but new is a char ** and str[i] is a char. Also, the first argument to strcat must be a valid string but new is uninitialized.
}
}
}
free(buffer);
return new;
new is a local array in this function. You're returning the address of its first element, which makes no sense: As soon as the function returns, all of its local variables are gone. You're returning an invalid pointer here.
Also, this is a type error: deblank is declared to return a char * but actually returns a char **.
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
This is a type error: *new is a char but deblank returns a char *.
puts(new);
puts takes a string, but new is essentially garbage at this point.
}
You can't use strcat like you did, it is intended to catenate a C-string at the end of another given one. str[i] is a char not a C-string (remember that a C-string is a contiguous sequence of chars the last being the NUL byte).
You also cannot compare strings with standard comparison operators, if you really need to compare strings then there is a strcmp function for it. But you can compare chars with standard operators as char is just a kind of integer type.
This should do the trick:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * deblank(const char str[]) {
char *buffer = malloc(strlen(str)+1); // allocate space to contains as much char as in str, included ending NUL byte
for (int i=0, j=0; i<strlen(str)+1; i++) { // for every char in str, included the ending NUL byte
if (str[i]!=' ') { // if not blank
buffer[j++] = str[i]; // copy
}
}
return buffer; // return a newly constructed C-string
}
int main(void){
char str[] = "This has spaces in it.";
char *new = deblank(str);
puts(new);
free(new); // release the allocated memory
}
So, not sure whether this helps you, but a C code doing the same as your Java code would look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *removeBlanks(const char *str)
{
char *result = malloc(strlen(str) + 1);
if (!result) exit(1);
const char *r = str;
char *w = result;
while (*r)
{
// copy each character except when it's a blank
if (*r != ' ') *w++ = *r;
++r;
}
*w = 0; // terminate the result to be a string (0 byte)
return result;
}
int main(void)
{
const char *str = "This has spaces in it.";
char *new = removeBlanks(str);
puts(new);
free(new);
return 0;
}
I would'nt recommend to name a variable new ... if you ever want to use C++, this is a reserved keyword.
I tried compiling with warnings enabled, here are some you should fix.
You need to include stdlib.h
char *new[strlen(str)] creates an array of char* not of char, so not really a string. Change it to char new[strlen(str)].
To check if str[i] is a space, you compare it to the space character ' ', not a string whose only character is a space " ". So change it to str[i]!=' '
strcat takes a string as the second argument and not a character, like you're giving it with str[i].
Also, what are you using buffer for?
Another mistake, is that you probably assumed that uninitialized arrays take zero values. The new array has random values, not zero/null. strcat concatenates two strings, so it would try to put the string in its second argument at the end of the first argument new. The "end" of a string is the null character. The program searches new for the first null character it can find, and when it finds this null, it starts writing the second argument from there.
But because new is uninitialized, the program might not find a null character in new, and it would keep searching further than the length of new, strlen(str), continuing the search in unallocated memory. That is probably what causes the segmentation fault.
There can be three approaches to the task.
The first one is to update the string "in place". In this case the function can look something like the following way
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char s[] )
{
size_t i = 0;
while ( s[i] and not isblank( s[i] ) ) ++i;
if ( s[i] )
{
size_t j = i++;
do
{
if ( not isblank( s[i] ) ) s[j++] = s[i];
} while( s[i++] );
}
return s;
}
int main(void)
{
char s[] = "This has spaces in it.";
puts( s );
puts( deblank( s ) );
return 0;
}
The program output is
This has spaces in it.
Thishasspacesinit.
Another approach is to copy the source string in a destination character array skipping blanks.
In this case the function will have two parameters: the source array and the destination array. And the size of the destination array must be equal to the size of the source array because in general the source array can not have blanks.
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char *s1, const char *s2 )
{
char *t = s1;
do
{
if ( not isblank( *s2 ) ) *t++ = *s2;
} while ( *s2++ );
return s1;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char s2[sizeof( s1 )];
puts( s1 );
puts( deblank( s2, s1 ) );
return 0;
}
The program output will be the same as shown above.
Pay attention to this declaration
char s2[sizeof( s1 )];
The size of the destination string in general should be not less than the size of the source string.
And at last the third approach is when inside the function there is created dynamically an array and pointer to the first element of the array is returned from the function.
In this case it is desirable at first to count the number of blanks in the source array that to allocated the destination array with the appropriate size.
To use the functions malloc and free you need to include the following header
#include <stdlib.h>
The function can be implemented as it is shown in the demonstrative program.
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <iso646.h>
char * deblank( const char *s )
{
size_t n = 1; /* one byte reserved for the terminating zero character */
for ( const char *t = s; *t; ++t )
{
if ( not isblank( *t ) ) ++n;
}
char *s2 = malloc( n );
if ( s2 != NULL )
{
char *t = s2;
do
{
if ( not isblank( *s ) ) *t++ = *s;
} while ( *s++ );
}
return s2;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char *s2 = deblank( s1 );
puts( s1 );
if ( s2 ) puts( s2 );
free( s2 );
return 0;
}
The program output is the same as for the two previous programs.
As for the standard C function strcat then it cats two strings.
For example
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
puts( strcat( s1, s2 ) );
return 0;
}
The destination array (in this case s1) must have enough space to be able to append a string.
There is another C function strncat in the C Standard that allows to append a single character to a string. For example the above program can be rewritten the following way
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
for ( size_t i = 0; s2[i] != '\0'; i++ )
{
strncat( s1, &s2[i], 1 );
}
puts( s1 );
return 0;
}
But it is not efficient to use such an approach for your original task because each time when the function is called it has to find the terminating zero in the source string that to append a character.
you can try recursively
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void deblank(const char* str, char *dest) {
if (!*str) {*dest = '\0';return;}
// when we encounter a space we skip
if (*str == ' ') {
deblank(str+1, dest);
return;
}
*dest = *str;
deblank(str+1, dest+1);
}
int main(void) {
const char *str = "This has spaces in it.";
char *output = malloc(strlen(str)+1);
deblank(str, output);
puts(output);
free(output);
}

garbage output after whitespace removing

i've this code:
int i =0;
char * str = "ar bitrary whitespace";
int whitespace=0,index;
for(index = 0;index < strlen(str);index++)
{
if(isspace(str[index]) != 0)
{
whitespace++;
}
}
char * tmp = (char *)calloc(strlen(str)-whitespace +1,sizeof(char));
memset(tmp,'\0',strlen(tmp)+1);
while(i < strlen(str))
{
if(isspace(str[i]) != 0)
{
i++;
continue;
}else if(isspace(str[i]) == 0)
{
strcat(tmp,&str[i]);
i++;
}
}
printf("\nnew string is: %s \n",tmp);
the problem is that the output is a string without the whitespace removed + some garbage character.
I've used memset to null terminate tmp,is there the problem?
The length of the source string could be calculated before this loop
for(index = 0;index < strlen(str);index++)
Otherwise if the code will not be optimized the function strlen will be called for each iteration of the loop. In fact using of the function is redundant for such a task.
This statement
memset(tmp,'\0',strlen(tmp)+1);
does not make sense because the call of calloc already initialized the memory with zeroes.
This statement
strcat(tmp,&str[i]);
also copies blanks from the source string after the position i. So it can write beyond the memory allocated for the array pointed to by the pointer tmp.
You can write a separate function that can look as it is shown in this demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
char * remove_blanks( const char *s )
{
size_t n = 0;
const char *p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) ++n;
} while ( *p++ );
char *t = malloc( n );
if ( t )
{
char *q = t;
p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) *q++ = *p;
} while ( *p++ );
}
return t;
}
int main(void)
{
char * str = "ar bitrary whitespace";
printf( "\"%s\"\n", str );
char *t = remove_blanks( str );
printf( "\"%s\"\n", t );
free( t );
}
The program output is
"ar bitrary whitespace"
"arbitrarywhitespace"
this is your problem
memset(tmp,'\0',strlen(tmp)+1);
strlen(tmp) works by looking for '\0' in tmp, you have a chicken and egg situation here.
You should not be doing a memset any way, just tack on a '\0' when you fnish copying
And dont use strcat, instead maintain a pointer to tmp and just do *p = str[i] then increment p
I will not read your question, you overwrite the '\0' terminator for sure.
Now that I read your question, it looks like you need to understand strings and arrays better,
Don't ever write while (i < strlen(str))
Don't use strcat() for adding a single character, you apparently did overwrite the '\0' there. Furthermore, don't ever use strcat() for concatenating more than to pieces of a string.
Also notable,
You memset() after calloc() which already initialized to 0. That means that you are enforcing something that is not necessary, and trying it twice as if it failed the first time which I can guarantee it didn't.
In fact, since you have used calloc() and all bytes pointed to by tmp are 0 then strlen(tmp) will return 0, thus your memset() is equivalent to
tmp[0] = '\0';
and you REALLY don't need initialize tmp except when you finally copy the actual bytes from str.
I always advice against calloc() for strings, because
You don't really need to initialize something twice.
You should be sure your code does take the terminating '\0' into account and not simply assume that it's there because you calloc()ed. That is a bug that you just hide with calloc() but it shows up at some point.
Try this and see if you can understand the reasons for my changes
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(void)
{
int whitespace;
int length;
char *str = "ar bitrary whitespace";
char *tmp;
whitespace = 0;
for (length = 0; str[length] != '\0'; ++length) {
if (isspace(str[length]) != 0) {
whitespace++;
}
}
tmp = malloc(length - whitespace + 1);
if (tmp == NULL)
return -1;
for (int i = 0, j = 0; str[i] != '\0'; ++i) {
if (isspace(str[i]) != 0)
continue;
tmp[j++] = str[i];
}
tmp[length - whitespace] = '\0';
printf("new string is: %s\n",tmp);
free(tmp);
return 0;
}

Resources