How to append characters to a string array in C - c

I'm very new to C and I'm trying to write a program that checks if a string contains any uppercase letters, and if it does, prints them out. I'm using https://www.onlinegdb.com/online_c_compiler# as my compiler (cause I don't have access to my personal computer right now) and after a test run, the results are (p.s. I know gets isn't safe):
main.c:16:5: warning: ‘gets’ is deprecated [-Wdeprecated-declarations]
/usr/include/stdio.h:638:14: note: declared here
main.c:(.text+0x26): warning: the `gets' function is dangerous and should not be used.
sTrInG
Contains Uppercase!
Uppercase Letters:0
...Program finished with exit code 0
Press ENTER to exit console.
In this case, I expect an output something like this:
Contains Uppercase!
Uppercase Letters: TIG
My script:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main()
{
char str[100];
gets(str);
int containsUpper = 0;
char upperLetters[100] = {0};
for (int i=0; i < strlen(str); i++) {
if (islower(str[i])) {
continue;
} else {
containsUpper = 1;
upperLetters[i] = str[i]; // is this the bad line?
}
}
if (containsUpper) {
printf("Contains Uppercase!\n");
printf("Uppercase Letters:");
printf("%zu\n", strlen(upperLetters)); // prints 0 so upperLetters is empty..?
for (int i=0; i < strlen(upperLetters); i++) {
printf("%c", upperLetters[i]);
}
} else {
printf("Does not contain Uppercase!");
}
return 0;
}

This loop
for (int i=0; i < strlen(str); i++) {
if (islower(str[i])) {
continue;
} else {
containsUpper = 1;
upperLetters[i] = str[i]; // is this the bad line?
}
}
1) is incorrect and 2) suffers from a bad style of programming.
You should append upper case letters to the character array upperLetters
consistently that you are not doing. Also if a character is not a lower case character it does not mean that the character is an upper case character. For example in general it can be a digit or a punctuation.
Also there is no need to call the function strlen. An argument of the function call should be cast to unsigned char. Otherwise it can occur that a call of the function will invoke undefined behavior.
The part of the loop with the continue statement is redundant.
The loop can look for example the following way
for ( size_t i = 0, j = 0; str[i] != '\0'; i++ )
{
if ( isupper( ( unsigned char )str[i] ) )
{
upperLetters[j++] = str[i];
}
}
containsUpper = upperLetters[0] != '\0';
If you need the number of uppercase letters in other part pf the program then the loop can look like
size_t n = 0;
for ( size_t i = 0; str[i] != '\0'; i++ )
{
if ( isupper( ( unsigned char )str[i] ) )
{
upperLetters[n++] = str[i];
}
}
if ( n )
{
printf( "Contains Uppercase!\n" );
printf( "Uppercase Letters: " );
printf("%zu\n", n );
for ( size_t i = 0; i < n; i++ )
{
printf( "%c", upperLetters[i] );
}
//…
Or instead of the loop
for ( size_t i = 0; i < n; i++ )
{
printf( "%c", upperLetters[i] );
}
you could just write
printf( "%s\n", upperLetters );
because the array was zero-initialized and as such it contains a string.
As the compiler reported the function gets is unsafe and is not supported by the C Standard. Instead use the function fgets.
For example
fgets( str, sizeof( str ), stdin );

I wouldn't consider numbers or characters like !?#%& to be uppercase letters, yet your program counts them as such. You absolutely must not use gets, but there is no reason in this case to replace it with fgets since your program is not fundamentally line-oriented. You simply don't care about lines. Just do:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int
main(void)
{
int c;
char upperLetters[100] = {0};
unsigned k = 0;
while( ( c = getchar()) != EOF && k < sizeof upperLetters ) {
if( isupper(c)) {
upperLetters[k++] = c;
}
}
if (k) {
puts("Contains Uppercase!");
printf("Uppercase Letters: %u\n%s\n", k, upperLetters);
} else {
fprintf(stderr, "Does not contain Uppercase!");
}
return k != 0;
}
(Note that the original program only looks at the first line of input. Whether that is a bug or intentional is not clear. If it is intentional, add a check and break out of the loop when after the first newline character is read.)

You want the loop to be:
int i, j=0;
for (i=0; i < strlen(str); i++) {
if (isupper((unsigned char)str[i])) {
upperLetters[j++] = str[i];
}
}
upperLetters[j]= '\0';
containsUpper = (j>0);
That is, keep a separate index of the upper letters array. And don't forget to terminate it.
A better way for the loop is:
int i, j, k;
for (i=0, j=0, k=strlen(str); i < k; i++) {
as this calls strlen only once.
EDIT: As user LxerLx pointed out, a character which is not a lower case letter does not have to be an upper case letter. I updated the loop for this.

Related

what's wrong with my code, it doesn't print the third sentence

Write a program that allows the user to enter a sentence. The program should then print the sentence starting with the third word.
For example it should print like:
Enter a sentence: Welcome to the course in programming
the course in programming
The problem with my code is that it only print the sentence starting with the second word instead of the thrird.And I wonder what is wrong with my code?
#include <stdio.h>
#include <string.h>
#define SIZE 100
int main(void)
{
char arr[SIZE];
char* p = NULL;
int count = 0;
printf("Enter a sentence:");
fgets(arr, SIZE, stdin);
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
}
if (count < 3 ) {
printf("The sentence is to short!\n");
}
else {
count = 0;
for (int i = 0; i < strlen(arr); i++) {
if (arr[i] == ' ') {
count++;
}
if (count == 2) {
p = &arr[i + 1];
}
}printf("%s\n", p);
}
return 0;
}
I do not know why the program outputs the sentence from the second word instead of the third word but in any case this for loop
for (int i = 0; i < strlen(arr); i++) {
if (arr[i] == ' ') {
count++;
}
if (count == 2) {
p = &arr[i + 1];
}
}
is incorrect because when count is equal to 2 then for each character in the string that is not a space the value of p is changed due to this if statement
if (count == 2) {
p = &arr[i + 1];
}
At least you need to insert a break statement like
if (count == 2) {
p = &arr[i + 1];
break;
}
Also the first for loop
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
}
invokes undefined behavior because instead of SIZE you need to use strlen( arr ).
And moreover you need to remove the trailing new line character '\n' from the string before calculating the number of words.
Apart from that the approach in any case is incorrect because it will not work when a string contains adjacent spaces.
You're trying to do to much with too much. Simplifying the code makes life easier.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define SIZE 100
int main(void) {
printf( "Enter a sentence:" );
/* ONE buffer to hold the input */
char arr[SIZE] = { 0 }; // define (and initialise) variables close to use
fgets( arr, SIZE, stdin );
/* ONE counter (index) to traverse the input */
int i = 0;
while( arr[i] && isspace( arr[i] ) ) i++; // skip over leading whitespace (not considered)
while( arr[i] && !isspace( arr[i] ) ) i++; // skip over the 1st "word" (if present )
while( arr[i] && isspace( arr[i] ) ) i++; // skip over intervening whitespace (if present )
while( arr[i] && !isspace( arr[i] ) ) i++; // skip over the 2nd "word" (if present )
while( arr[i] && isspace( arr[i] ) ) i++; // skip over intervening whitespace (if present )
if( arr[i] )
printf( "%s\n", arr + i );
else
printf("The sentence is to short!\n");
return 0;
}
Enter a sentence: Now is the time for all good men
the time for all good men
You can see the repetitive code (testing for spaces, then testing for non-spaces.) This could be "packaged-up" in a function that takes a couple of parameters. I'll leave that as an exercise. (Perhaps look into strpbrk() strspn() and strcspn()...)

Reverse line function in C works unpredictably

I'm doing the exercises from C Programming Language, the one where you need to make a function to reverse a line. So I did and it works sometimes. But only some times. With the same test it gives different results. I really don't get it, and would appreciate some help. 3 tries out of 4 it would print around 150 spaces and 1 out of 4 it would print the reversed line just like I wanted, though with some junk in the end for some reason.
I was thinking of doing it with pointers, but couldn't figure them out as of now.
Here's my code:
#include <stdio.h>
void reverse(char theline[150]){
int i, j;
char tmp[150];
for (i = 0; theline[i] != 0; i++){
tmp[i] = theline[i];
}
for (j = 0; i >= 0; j++){
theline[j] = tmp[i];
i--;
}
}
int main() {
char line[150];
char c;
int counter = 0;
do {
counter = 0;
while (((c = getchar()) != '\n') && (c != EOF)) { //one line loop
line[counter] = c;
counter++;
}
if (counter > 80){
reverse(line);
printf("%s\n", line);
}
}
while (c != EOF);
return 0;
}
I compile it with "gcc -g -Wall program -o test" and the compiler doesn't give me any errors or warnings. My OS is Ubuntu and I test it with "./test < longtext.txt". This text file has a few lines of different length.
After this loop
while (((c = getchar()) != '\n') && (c != EOF)) { //one line loop
line[counter] = c;
counter++;
}
the character array line does not contain a string because the stored characters are not appended with the terminating zero character '\0'.
So this loop within the function
for (i = 0; theline[i] != 0; i++){
tmp[i] = theline[i];
}
invokes undefined behavior.
You need to append the array with the terminating zero character '\0'.
But even if the passed character array will containe a string the second for loop
for (i = 0; theline[i] != 0; i++){
tmp[i] = theline[i];
}
for (j = 0; i >= 0; j++){
theline[j] = tmp[i];
i--;
}
writes the terminating zero character '\0' in the first position if the array theline. As a result you will get an empty string.
Also the function shall not use the magic number 150 and an auxiliary array.
Pay attention to that the variable c should be declared as having the type int. In general the type char can behave either as the type signed char or unsigned char depending on compiler options. If it will behave as the type unsigned char then this condition
c != EOF
will always evaluate to true.
Without using standard C string functions the function can be declared and defined the following way
char * reverse( char theline[] )
{
size_t i = 0;
while ( theline[i] != '\0' ) i++;
size_t j = 0;
while ( j < i )
{
char c = theline[j];
theline[j++] = theline[--i];
theline[i] = c;
}
return theline;
}
Here is a demonstration program
#include <stdio.h>
char * reverse( char theline[] )
{
size_t i = 0;
while ( theline[i] != '\0' ) i++;
size_t j = 0;
while ( j < i )
{
char c = theline[j];
theline[j++] = theline[--i];
theline[i] = c;
}
return theline;
}
int main( void )
{
char s[] = "Hello World!";
puts( s );
puts( reverse( s ) );
}
The program output is
Hello World!
!dlroW olleH
Why muck-around with reversing a buffer when you can simply store-up the entered characters as they arrive.
#include <stdio.h>
int main() {
for( ;; ) {
char line[ 150 ];
int c, counter = sizeof line;
line[ --counter ] = '\0';
// NB: EOF is an int, not a char
while( ( c = getchar() ) != '\n' && c != EOF && counter > 0 )
line[ --counter ] = (char)c;
printf( "%s\n\n", line + counter );
counter = sizeof line;
}
return 0;
}
Output:
the quick
kciuq eht
Once upon a time in a land far awy
ywa raf dnal a ni emit a nopu ecnO
I wish I was what I was when I wished I was what I am now.
.won ma I tahw saw I dehsiw I nehw saw I tahw saw I hsiw I

Pangram in C using functions

When I input The quick brown fox jumps over the lazy dog, the following program prints not a pangram. Yet, I expect s to be 26 and printf("pangram") to be executed. What am I doing wrong?
#include <ctype.h>
#include <stdio.h>
#include <string.h>
char findpan(char arr[]) {
int i, j, count = 0;
for (i = 0; i < strlen(arr); i++) {
if (isalpha(arr[i]))
count++;
}
for (i = 0; i < strlen(arr); i++) {
for (j = i + 1; j < strlen(arr); j++) {
if (arr[i] == arr[j])
count--;
}
}
return (count);
}
int main() {
int s;
char str[60];
fgets(str, 60, stdin);
s = findpan(str);
if (s == 26)
printf("pangram");
else
printf("not a pangram");
return 0;
}
If I have understood what you are trying to do then these nested loops
for (i = 0; i < strlen(arr); i++) {
for (j = i + 1; j < strlen(arr); j++) {
if (arr[i] == arr[j])
count--;
}
}
are incorrect. Let's assume that you have string "AAA". So after the preceding loop count will be equal to 3.
Now after these nested loops count will be equal to 0 instead of 1. That is when i = 0 then for j = 1 and j = 2 arr[j] is equal to arr[i]. So count will be decreased two times. When i = 1 then for j = 2 again arr[j] = arr[i] and count will be decreased one more.
Also it seems you should ignore cases of letters.
I can suggest the following function implementation as it is shown in the demonstrative program below.
#include <stdio.h>
#include <ctype.h>
size_t findpan( const char *s )
{
size_t count = 0;
for ( const char *p = s; *p; ++p )
{
if ( isalpha( ( unsigned char ) *p ) )
{
char c = tolower( ( unsigned char )*p );
const char *q = s;
while ( q != p && c != tolower( ( unsigned char )*q ) ) ++q;
if ( q == p ) ++ count;
}
}
return count;
}
int main(void)
{
printf( "%zu\n", findpan( "The quick brown fox jumps over the lazy dog" ) );
return 0;
}
The program output is
26
Without using pointers the function can look the following way
size_t findpan( const char *s )
{
size_t count = 0;
for ( size_t i = 0; s[i] != '\0'; i++ )
{
if ( isalpha( ( unsigned char ) s[i] ) )
{
char c = tolower( ( unsigned char )s[i] );
size_t j = 0;
while ( j != i && c != tolower( ( unsigned char )s[j] ) ) ++j;
if ( j == i ) ++count;
}
}
return count;
}
Simple Solution?
Here a Simple solution, I made the guess that you probably just want to know if it is or it isn't a pangram and so i've changed your function to a boolean one:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool findpan(char arr[]) {
int i,j;
for (i = 'a'; i < 'z'; ++i) { // goes through the alphabet
for (j = strlen(arr); j > 0; j--) // goes through the arr[]
if (tolower(arr[j]) == i) // checks if the letter exists
break; // breaks the inner for-loop if letter found
if (j == 0) // if letter not found
return false;
}
return true;
}
int main() {
bool isPangram;
char str[60];
fgets(str, 60, stdin);
isPangram = findpan(str);
if (isPangram)
printf("pangram");
else
printf("not a pangram");
return 0;
}
Explanation
'a' to 'z' represent the range of the lowercase letters in Decimal numbers, in the ASCII table:
for (i = 'a'; i < 'z'; ++i)
tolower converts arr[j] character to lowercase and then compares it to i:
if (tolower(arr[j]) == i)
stdbool.h is introduced for the use of bool aka boolean:
#include <stdbool.h>
Limiting myself to plain ASCII you can create a simple array, with one element per letter and each element initialized to zero. Then loop over the string, and for each letter convert it to an index into the array and increase the corresponding elements value.
Once done with the input string, you loop over the array, and increase a counter for every non-zero value, and return that.
Perhaps something like this:
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char input[512];
if (!fgets(input, sizeof input, stdin))
return 1; // Failed to read input
int letters[26] = { 0 }; // 26 letters in the English alphabet
for (unsigned i = 0; input[i] != '\0'; ++i)
{
if (isalpha(input[i]))
{
// Limiting myself to e.g. plain ASCII here
++letters[tolower(input[i]) - 'a'];
}
}
// Count the number of non-zero elements in the letters array
unsigned counter = 0;
for (unsigned i = 0; i < 26; ++i)
{
counter += letters[i] != 0;
}
// Print result
printf("Counter = %d\n", counter);
}
With your example input (The quick brown fox jumps over the lazy dog) it outputs
Counter = 26
This does only a single pass over the input string, and then a single pass over the letters array. No nested loop, no multiple passes over the input string.
If we assume 8 bit characters and can stand allocating 256 bytes on the stack temporarily, then this is both readable, compact and fairly efficient:
bool is_pangram (const char* str)
{
char used [256]={0};
for(; *str!='\0'; str++)
{
used[*str]=1;
}
return memchr(&used['a'], 0, 26)==NULL; // 26 letters in the alphabet
}
The 256 byte zero-out might seem inefficient, but the mainstream x86 compilers run that in 16 instructions. This function also makes no assumptions of adjacency of 'a' to 'z'. To add support for upper case, simply do used[tolower(*str)]=1; though that might introduce a lot of branching.
Test code:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
bool is_pangram (const char* str)
{
char used [256]={0};
for(; *str!='\0'; str++)
{
used[*str]=1;
}
return memchr(&used['a'], 0, 26)==NULL;
}
int main (void)
{
const char* test_cases[] =
{
"",
"hello, world!",
"the quick brown fox jumps over the lazy dog",
"the quick brown cat jumps over the lazy dog",
"junk mtv quiz graced by fox whelps",
"public junk dwarves hug my quartz fox",
};
for(size_t i=0; i<sizeof test_cases/sizeof *test_cases; i++)
{
printf("\"%s\" is %sa pangram\n", test_cases[i], is_pangram(test_cases[i])?"":"not ");
}
return 0;
}

Why is my program to reverse its input a line at a time not working?

I was trying to write a program that reverses its input a line at a time. I thought I had done it successfully, however it sometimes doesn't give the desirable output (this happens when I put an input with a smaller length than the one I put previously). I am new to this and I was wondering what can I do to solve this issue.
Program:
#include <stdio.h>
#define MAXLINE 1000
void reverse(char o[], char l[]);
int mgetline(char line[]);
int main(void){
int len;
char line[MAXLINE];
char rev[MAXLINE];
while((len = mgetline(line)) > 0){
reverse(rev, line);
printf("%s\n",rev);
}
return 0;
}
int mgetline(char s[])
{
int c,i;
for(i = 0; ((c=getchar())!=EOF) && (c!='\n'); ++i)
s[i] = c;
if (c == '\n')
{
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
void reverse(char revi[], char liner[])
{
int i, c, j;
for(i=0;liner[i]!='\0';++i)
;
--i;
if(liner[i]=='\n')
--i;
for(j=0; j<=i ; ++j){
c = i - j;
revi[j] = liner[c];
}
--j;
}
Since you not terminating the revi string at the end, therefore it will print the leftout characters from the previous result if the new input is smaller. Fix it by adding this
revi[j] = '\0';
at the end of the reverse function and delete that last --j;.
The function reverse does not build a string that is it does not append the terminating zero '\0' to the result string.
The second parameter of the function should have the qualifier const because it is not changed in the function.
As all standard C string functions this function should return pointer to the result string.
And it is better to name the function like reverse_copy instead of reverse because the name reverse is usually used when a container is reversed "in place".
It can look the following way
char * reverse_copy( char revi[], const char liner[] )
{
size_t n = 0;
while ( liner[n] ) ++n;
if ( n != 0 && liner[n-1] == '\n' ) --n;
size_t i = 0;
while ( n != 0 ) revi[i++] = liner[--n];
revi[i] = '\0';
return revi;
}

I need to write a C program that calls a given function to count the number of characters and digits in a file

I need to write a C program that counts the number of characters and digits in a file. I believe my best attempt is close, but the program must call a given function, mostfrequent(), and I cannot figure out how to implement it into my main so that they work together. Any help would be greatly appreciated. Thank you.
// this is the function my program is required to use.
int mostfrequent(int *a, int length) {
int index = 0;
int max = a[0];
int i;
for (i = 1; i < length; i++) {
if (a[i] > max) {
max = a[i];
index = i;
}
}
return index;
}
// this is my closest attempt at a working program so far, but it does
// not call mostfrequent() which I need it to do.
int main() {
typedef FILE *ptr_file;
int x, i, j;
int length;
char c;
char ch[1000];
int a = 65;
c = getc(ptr_file);
ptr_file = fopen("file.txt", "r");
if (!ptr_file)
return 1;
while (c != EOF) {
scanf(ptr_file, "%s", ch[i]);
i++;
fclose(ptr_file);
}
for (i = 0; i < length; i++) {
for (j = 0; j < length; j++) {
if (a < 116) {
if (char(a) == 'ch[j]')
char max_char_temp=(char)a
count_temp++;
}
if (count_temp > count) {
count = count_temp;
max_char = max_char_temp;
}
return 0;
}
regarding the question: when to call the most_frequent() function.
After you have created an array (which would be 36 entries long of integers), initialize that array to all zeros, then incremented the appropriate entry for each character read from the input file. (note 36 entries allows for a...z + 0...9 so all other characters read from the file should be discarded.
Then pass the array and 36 to the most_frequent() function
then code similar to the following could do the job:
#include <stdio.h>
#include <stdlib.h>
#include <ctypes.h> // isalpha(), isdigit(), toupper()
#define NUM_ALPHA (26)
int main( void )
{
int array[36] = {'\0'};
//...open file, etc
//then assure all processed characters are upper case or numeric
// and update the count in the array
int ch;
while( ch = getc( file ) != EOF && '\n' != ch)
{
if ( isalpha(ch) )
{
ch = toupper(ch);
array[ch - 'A']++;
}
else if (isdigit(ch) )
{
array[ (ch-'0') + NUM_ALPHA ]++;
}
}
int index = mostfrequent( array, sizeof(array)/sizeof(array[0]);
//... do what is needed with 'index' for instance
printf( "the highest occurring char is:" );
if( index < NUM_ALPHA )
{
printf( "%c\n", index+'A' );
}
else
{
printf( "%d\n", (index-NUM_ALPHA)+'0');
}
fclose( file );
return 0;
}
however, note that mostfrequent() only returns the index to the first entry encountered with the max value, when there are multiple entries with the same max value.

Resources