C language finding T or t in the entered word - c

You are interested in finding words that contain the letter 't' or 'T' in the first half of the word (including the middle letter if there is one). Specifically, if the first half of the word does contain a 't' or a 'T', your program should output a 1. If the first half does not contain the letter 't' or 'T', but the second half does, then your program should output a 2. Otherwise, if there is no 't' or 'T' in the word at all, your program's output should be -1. You may assume that the word entered does not have more than 50 letters.
#include <stdio.h>
int main (void){
int i=0;
char word[51];
int l=0;
int half;
char found1='T';
char found2='t';
scanf("%s",word);
while (word[i]!='\0'){
i++;
}
half=i/2;
while(word[l]!='\0'){
scanf("%s",word);
l++;
if((word[l]!=half)&&((word[l]==found1)&&(word[l]==found2))){
printf("1");
}
if((word[l]!=i)&&((word[l]==found1)&&(word[l]==found2))&&(word[l]>=half)){
printf("2");
}
}
if(i%2!=0){
printf("1");
}else{
printf("-1");
}
return 0;
}

Break it down as simple as possible:
Find the index of a T or t in the string.
Did you find it?
no, output 0 and quit
Did you find it in the first half?
no, output 2 and quit
Output 1 and quit.
Get input with:
char word[52];
fgets(word, sizeof(word), stdin);
Determine the length of a string with:
int n = strlen(word) - 1; // Don’t count the '\n' at the end of the input
Remember that integer division rounds toward zero, so:
int half = (n + 1) / 2;
Anything < half is in the first half. Anything ≥ half is in the second half.

While you can do the comparisons character-by-character, C provides char *strpbrk(const char *s, const char *accept) that will provide the position in s of the first byte (character) in accept. Using "Tt" as your accept and word as s you receive a pointer to the first occurrence of 'T' or 't' in word, e.g.
#include <stdio.h>
#include <string.h>
#define MAXWORD 51
int main (void) {
char word[MAXWORD], *ptr2t = NULL; /* storage and ptr to t or T */
size_t len = 0, pos = 0, mid = 0; /* word len, tT pos & mid pos */
fputs ("\nenter word (50 char max): ", stdout); /* prompt */
if (!fgets (word, MAXWORD, stdin)) { /* read up to 50 chars */
puts ("(user canceled input)");
return 0;
}
word[(len = strcspn (word, "\n"))] = 0; /* save len, trim \n from end */
if (!(ptr2t = strpbrk (word, "Tt"))) { /* locate 't' or 'T' in word */
puts ("-1");
return 0;
}
if (len == 1) { /* if length 1, no determination of half is possible */
puts ("(length is 1, half determination not possible)");
return 0;
}
mid = len / 2; /* get mid position */
pos = ptr2t - word; /* get postion in word */
#ifdef ODD_MID_IN_1ST /* if considering mid character in odd */
if (len & 1) { /* length part of 1st half, add 1 to */
mid += 1; /* mid. */
}
#endif
puts (pos < mid ? "1" : "2"); /* output 1 in 1st half, 2 otherwise. */
}
Following from my comments, you also have to handle the case where the length of the input is 1 as there can be no half determination (handle as you want), and you have to determine which half the mid-character in an odd length word belongs in. By default the mid-character belongs to the second-half. The define ODD_MID_IN_1ST will change the behavior to place the mid-character in the first-half (up to you).
Compile
With gcc you can use:
gcc -Wall -Wextra -pedantic -Wshadow -std=c11 -Ofast -o bin/tin1sthalf tin1sthalf.c
Where the options -Wall -Wextra -pedantic -Wshadow enable full warnings (for the most part) and checks whether there are shadowed variables. -std=c11 specifies the language standard as C11 and -Ofast is full optimization for gcc >= 4.6, prior to that the optimization levels were limited to O0, O1, O2 and O3. Add -Werror to treat warnings as errors (recommended). The executable output -o will be placed in the bin/ directory (change as needed).
Always compile with full-warnings enabled (every compiler provides options to enable warnings) and do not accept code until it compiles without warning (-Werror keeps you from cheating...)
Example Use/Output
Showing the various special case handling and general output of the program:
$ ./bin/tin1sthalf
enter word (50 char max): t
(lenght is 1, half determination not possible)
$ ./bin/tin1sthalf
enter word (50 char max): t_
1
$ ./bin/tin1sthalf
enter word (50 char max): _t
2
$ ./bin/tin1sthalf
enter word (50 char max): _t_
2
$ ./bin/tin1sthalf
enter word (50 char max): ___
-1
$ ./bin/tin1sthalf
enter word (50 char max): _T__
1
$ ./bin/tin1sthalf
enter word (50 char max): __t_
2
There are many, many ways to approach this problem. No one any more right than the other if they comply with the language standard or POSIX standard if applicable. The only difference is one of efficiency. As you learn to code, your focus should be on getting the code working and not worrying about micro optimizations early. Once you have your full program working, then profile it and worry about code optimizations at that point.
Let me know if you have questions.

It may be instructive to consider a C program with no #include's.
int main(int argc, char *argv[])
/*
* Assume the input word is argv[1], empty word (not containing T) if
* no argument. The result is in the return value of the main
* function, so we don't need to include stdio to print it.
*/
{
if (argc < 2)
return -1; // no T
char *word = argv[1];
int found = -1, length = 0, ch;
while ((ch = *(unsigned char*)word++) != 0)
{
++length;
if (found < 0 && (ch == 't' || ch == 'T'))
found = length;
}
if (found < 0)
return -1; // no T
return 2*(found -1) < length? 1: 2;
}
When testing, -1 appears as 255:
$ gcc -W -Wall -g so.c
$ ./a.out t; echo $?
1
$ ./a.out o; echo $?
255
$ ./a.out to; echo $?
1
$ ./a.out oto; echo $?
1
$ ./a.out ot; echo $?
2
However, non-ASCII characters don't work as expected. In the following, the first character of the argument is hwair, which takes four bytes in UTF-8 encoding:
$ ./a.out 𐍈tooo; echo $?
2

It is sufficient to detect 2 things:
Length of word.
Offset of first t or T.
The "word" does not need to be stored, just its end detected.
No need for a small (50) letter limit.
#include <ctype.h>
#include <stdio.h>
int main() {
unsigned long long word_length = 0;
unsigned long long t_offset = 0; // First letter in a word is at offset 1.
int ch;
do {
ch = getchar();
if (isalpha(ch)) {
word_length++;
if ((ch == 't' || ch == 'T') && (t_offset == 0)) {
// Record offset of first "Tee".
t_offset = word_length;
}
} else {
// Detect end-of-word
if (word_length > 0) {
const char *output = "-1";
if (t_offset > 0) {
if (t_offset <= (word_length+1)/2) output = "1";
else output = "2";
}
puts(output);
word_length = 0;
t_offset = 0;
}
}
} while (ch != EOF);
}

Related

How to find number of occurrences in array of chars in C?

I am trying to enter a word, and get how many times the letters were typed.
Say my input is "hello"
my output would be: h = 1, e = 1 l = 2 etc.
I am very close to getting it right, but I have a small issue with this code:
#include <stdio.h>
#include <string.h>
void find_frequency(char s[], int count[]) {
int c = 0;
while (s[c] != '\0') {
if (s[c] >= 'a' && s[c] <= 'z' )
count[s[c]-'a']++;
c++;
}
}
int main()
{
char string[100];
int c, count[26] = {0};
printf("Input a string\n");
gets(string);
find_frequency(string, count);
printf("Character Count\n");
for (c = 0 ; c < 26 ; c++)
if(count[c] > 0)
printf("%c : %d\n", c + 'a', count[c]);
return 0;
}
This code does half of the job, but not all.
It's output is in alphabetical order. How can i change it to give me an output of just the chararray that is input?
As Ry- suggested in this comment you could iterate back over the original string and use the chars as indices into your frequency table. Something like the following:
int len_string = strlen(string);
for (c=0; c<len_string; c++) {
char ch = string[c];
printf("%c: %d, ", ch, count[ch-'a']);
}
This won't completely match your expected output, since this code will output l: 2 twice, but that raises the question:
What is your expected output when you have a string like abba? a:2, b:2? a:1, b:2, a:1? a: 2, b:2, a:2? It's hard to help when you ask such an ambiguous question.
#include <stdio.h>
#include <string.h>
size_t ASCIIfreq[256];
void CountASCII(void *buff, size_t size)
{
unsigned char *charsptr = buff;
memset(ASCIIfreq, 0, sizeof(ASCIIfreq));
while(size--)
{
ASCIIfreq[*charsptr++]++;
}
}
void print(int printall)
{
for(size_t index = 0; index < 256; index++)
{
if(ASCIIfreq[index] || printall)
{
printf("The %03zu (0x%02zx) ASCII - '%c' has occured in the buffer %zu time%c\n",
index, index, (index > 32 && index < 127) ? (char)index : ' ',
ASCIIfreq[index], ASCIIfreq[index] == 1 ? ' ' : 's');
}
}
}
int main()
{
char teststring[] = "i am trying to enter a word, and get how many times the letters were typed. Say my input is \"hello\" my output would be: h = 1, e = 1 l = 2 etc.I am very close to getting it right, but i have a small issue with this code";
CountASCII(teststring, sizeof(teststring));
print(0);
return 0;
}
It's not clear what you mean by:
How can i change it to give me an output of just the chararray that is input?
Because that's exactly what you're doing in any case: Inputting a char array to the function; which is updated with numbers alphabetically; and later output as is.
So I'm guessing that you want to output the counts in the same order that each char was first encountered?
Solution
This will require a bit more work. You could keep a second array tracking the the order each character is encountered within find_frequency. But then that simple clean function starts doing too much.
So consider rather tweaking how you do the output:
void output_frequency(char s[], int count[]) {
int c = 0;
//loop s for the output
while (s[c] != '\0') {
if (s[c] >= 'a' && s[c] <= 'z' ) {
//found a character, report the count only if not reported before
if (count[s[c]-'a'] > 0) {
printf("%c : %d\n", s[c], count[s[c] - 'a']);
count[s[c]-'a'] = 0; //so you don't report this char again
}
}
c++;
}
}
If you are attempting to get an in-order count instead of a count in alphabetical order, you simply need to coordinate the indexes of your count array with the order of characters in your input buffer. To do that, simply loop over all characters in your input buffer and make a second pass counting the number of times the current character occurs. This will give you an in-order count of the number of times each character occurs, e.g.
#include <stdio.h>
#include <string.h>
#define COUNT 128
#define MAXC 1024
int main (void) {
char buf[MAXC] = ""; /* buffer to hold input */
int count[COUNT] = {0}; /* array holding inorder count */
fputs ("enter string: ", stdout); /* prompt for input */
if (!fgets (buf, MAXC, stdin)) { /* read line into buf & validate */
fputs ("error: EOF, no valid input.\n", stderr);
return 1;
}
/* loop over each character not '\n' */
for (int i = 0; buf[i] && buf[i] != '\n'; i++) {
char *p = buf; /* pointer to buf */
size_t off = 0; /* offset from start of buf */
while ((p = strchr (buf + off, buf[i]))) { /* find char buf[i] */
count[i]++; /* increment corresponding index in count */
off = p - buf + 1; /* offset is one past current char */
}
}
for (int i = 0; count[i]; i++) /* output inorder character count */
printf (i ? ", %c: %d" : "%c: %d", buf[i], count[i]);
putchar ('\n'); /* tidy up with new line */
return 0;
}
(note: strchr is used for convenience to simply find the next occurrence of the current character within the string and then off (offset) is used to start the search with the following character until no other matches in the string are found. You can simply use an additional loop over the characters in the buffer if you like.)
Example Use/Output
$ /bin/charcnt_inorder
enter string: hello
h: 1, e: 1, l: 2, l: 2, o: 1
However, this does recount each character and give the count again if the character is duplicated, (e.g. l: 2, l: 2 for each 'l'). Now it is unclear from:
"my output would be: h = 1, e = 1 l = 2 etc."
what you intended in that regard, but with just a little additional effort, you can use a separate index and a separate array to store the first instance of each character (in say a chars[] array) along with the count of each in your count[] array and preserve your inorder count while eliminating duplicate characters. The changes needed are shown below:
#include <stdio.h>
#include <string.h>
#define COUNT 128
#define MAXC 1024
int main (void) {
char buf[MAXC] = "",
chars[COUNT] = ""; /* array to hold inorder chars */
int count[COUNT] = {0};
size_t cdx = 0; /* add count index 'cdx' */
fputs ("enter string: ", stdout);
if (!fgets (buf, MAXC, stdin)) {
fputs ("error: EOF, no valid input.\n", stderr);
return 1;
}
for (int i = 0; buf[i] && buf[i] != '\n'; i++) {
char *p = buf;
size_t off = 0;
chars[cdx] = buf[i]; /* store in chars array */
if (i) { /* if past 1st char */
int n = i;
while (n--) /* simply check all before */
if (buf[n] == buf[i]) /* if matches current */
goto next; /* bail and get next char */
}
while ((p = strchr (buf + off, buf[i]))) {
count[cdx]++; /* increment count at index */
off = p - buf + 1;
}
cdx++; /* increment count index */
next:; /* goto label to jump to */
}
for (int i = 0; count[i]; i++)
printf (i ? ", %c: %d" : "%c: %d", chars[i], count[i]);
putchar ('\n');
return 0;
}
Example Use/Output
$ /bin/charcnt_inorder2
enter string: hello
h: 1, e: 1, l: 2, o: 1
or
$ ./bin/charcnt_inorder2
enter string: amarillo
a: 2, m: 1, r: 1, i: 1, l: 2, o: 1
Now your 'l' is only reported once with the correct count.
Note, in each example you should do additional validation to insure the entire input fit within your buffer, etc... The count (and chars) array were sized at 128 to cover the entire range of ASCII values. Don't skimp on buffer size. If you explicitly limit your input to UPPERcase or lowercase -- then you can limit your count size to 26, otherwise you need to consider the additional characters and punctuation that will be encountered. The same applies to your input buffer. If you anticipate you max input would be 500 chars, double it (generally to next available power of two, no real requirement for powers of two, but you are likely to see it that way).
Bottom line, I'd rather be 10,000 characters too long that one character too short... leading to Undefined Behavior.
Lastly, as mentioned in my comment never, never, never use gets. It is so insecure it has been removed from the C standard library in C11. Use fgets or POSIX getline instead.
Look things over and let me know if you have further questions.

C language - counting number of different vowels with no pointers or additional functions

I got this exercise that I haven't been able to solve, the point is to create a program where you type in a text, then the program analyzes each word of the text and counts the vowels of each word, then the program returns in screen the number of words that have 3 or more different vowels, and by different I mean, it doesn't matter if the word has 3 "a", it only count as one (the word has the vowels "a", it doesn't matter how many times), so for example, the word "above" has 3 vowels, the word "been" has 1 vowels, the word "example" has 2 vowels. The vowels can be upper case or lower case, it doesn't matter, and here is the tricky part: It cannot contain any pointers or functions made by us.
what i did was asking the user to enter word by word so the program analyze each word, and then at the end returns the number of words that contain 3 or more vowels, but I feel like there must be an easier way where the user can type a complete paragraph or text, then the program analyzes each word and return the number of words that have 3 or more different vowels.
Anyway, my code is as follows, any suggestions would be appreciated:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
main() {
int vowels, text, words, c, total=0,a=0,e=0,i=0,o=0,u=0;
printf ("How many words does your text has? ");
scanf("%d",&words);
for(c=1;c<=words;c++){
printf("Type your word %d, after that press enter, then press 'control' and 'z' at the same time, and then press enter again: \n", c);
while (EOF != (text=getchar())){
if (text == 'a' || text == 'A'){
a++;
if (a >=2){
a = 1;
}
}
if (text == 'e' || text == 'E'){
e++;
if (e >=2){
e = 1;
}
}
if (text == 'i' || text == 'I'){
i++;
if (i >=2){
i = 1;
}
}
if (text == 'o' || text == 'O'){
o++;
if (o >=2){
o = 1;
}
}
if (text == 'u' || text == 'U'){
u++;
if (u >=2){
u = 1;
}
}
}
vowels = a+e+i+o+u;
if(vowels >=3){
total = total +1;
}
a=0,e=0,i=0,o=0,u=0;
vowels = 0;
}
printf("\n\nThe total of words with 3 or more vowels is: %d", total);
printf("\n");
total=0;
return 0;
}
In order to read and analyze a single word, or a paragraph words to determine the number of words that contain at least three different vowels (of any case), this is one of the rare times when reading input with scanf (using the '%s' format specifier) actually is a reasonable choice.
Recall the '%s' format specifier will read characters up to the first whitespace. That gives you a simple way to read a word at a time from stdin. To end input, the user simply need to generate an EOF by entering ctrl+d (or ctrl+z on windows). This satisfies your paragraph requirement.
For parsing, you can take advantage of converting each character to lower case to simplify checking for vowels. Using a frequency array of 5 elements provides a simple way to track the number of different vowels found in each word. Then a final test to see if the number of vowels found equals the required number is all you need before incrementing your total word count for words with three different vowels.
A simple implementation would be something similar to:
#include <stdio.h>
enum { NREQD = 3, NVOWEL = 5, MAXC = 128 }; /* declare constants */
int main (void) {
char word[MAXC] = ""; /* word buffer */
size_t wordcnt = 0; /* words with 3 different vowels */
printf ("enter a word(s) below, [ctrl+d on blank line to end]\n");
for (;;) {
int vowels[NVOWEL] = {0}, /* frequency array */
vowelcnt = 0, /* vowels per-word */
rtn; /* scanf return */
if ((rtn = scanf ("%127s", word)) == EOF) /* chk EOF */
break;
for (int i = 0; word[i]; i++) { /* loop over each char */
if ('A' <= word[i] && word[i] <= 'Z') /* check upper */
word[i] ^= 'a' - 'A'; /* convert to lower */
switch (word[i]) { /* check if vowel */
case 'a': vowels[0] = 1; break;
case 'e': vowels[1] = 1; break;
case 'i': vowels[2] = 1; break;
case 'o': vowels[3] = 1; break;
case 'u': vowels[4] = 1; break;
}
}
for (int i = 0; i < NVOWEL; i++) /* loop over array */
if (vowels[i]) /* check index */
vowelcnt++; /* increment vowelcnt */
if (vowelcnt >= NREQD) /* do we have at least 3 vowels? */
wordcnt++; /* increment wordcnt */
}
printf ("\nThere are %zu words with %d different vowels.\n",
wordcnt, NREQD);
}
Example Use/Output
$ ./bin/vowelcnt
enter a word(s) below, [ctrl+d on blank line to end]
Everyone Understands That The Dictionary Doesn't Track
Words That Contain Vowels Like It Does Etimology.
There are 4 words with 3 different vowels.
Look things over and let me know if you have further questions.
You can use fgets to read a whole line. I don't know how you define a
paragraph though, do you mean just a long text or a collection of lines? You can
copy & paste multiple lines in the console and if you loop using fgets, then
you get all the lines. But allowing the user to enter multiple lines at once,
it's more tricky, because you should know how many lines the user will input.
That's why I'd say focus on reading the text line by line.
Your solution reads characters by characters and you are ignoring non-vowels.
That's OK, but you are not detecting words like you should do. The for loop
makes no sense, because in the first iteration you enter in a while loop that
is only going to leave when there are no more characters to read from stdin.
So the next iteration of the for loop will not enter the while loop and you
won't be reading anything any more.
You are also repeating too much code, I know you assignment says not to use your
own functions, but this can be improved with a simple look up table by creating
an array of chars using the characters as an index for the array. I'll explain
that in the code.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
int main(void)
{
char line[1024];
// initializing look ups with 0
int lookup_vowels[1 << CHAR_BIT] = { 0 };
// using 'a', 'e' as index for the lookup table
// if you want to know if a character is a vowel,
// lookup_vowels[character] will be 1 if character is
// a vowel, 0 otherwise
lookup_vowels['a'] = lookup_vowels['e'] = lookup_vowels['i'] =
lookup_vowels['o'] = lookup_vowels['u'] = 1;
// for parsing word with strtok
const char *delim = " \t\r\n";
int num_of_words = 0;
printf("Enter some text, to end input press ENTER and then CTRL+D\n");
while(1)
{
if(fgets(line, sizeof line, stdin) == NULL)
break;
// parsing words
char *word = strtok(line, delim);
if(word == NULL)
continue; // the line has only delimiters, ignore it
do {
// will be access with the same principle as the lookup
// table, the character is the index
int present[1 << CHAR_BIT] = { 0 };
size_t len = strlen(word);
for(size_t i = 0; i < len; ++i)
{
// I'll explain later the meaning
int c = tolower(word[i]);
if(lookup_vowels[c])
present[c] = 1; // set the present for a vowel to 1
}
int count = present['a'] + present['e'] + present['i'] + present['o']
+ present['u'];
if(count > 2)
{
printf("'%s' has more than three distinct vowels\n", word);
num_of_words++;
}
} while((word = strtok(NULL, delim)));
}
printf("The number of word with three or more distinct vowels: %d\n", num_of_words);
return 0;
}
So let me quickly explain some of the technique I use here:
The lookup table is an array of size 256 because a char is 8-bit1
value and can have 256 different values (range [0,255]). The idea is that this
array is initialized with 0 overall (int lookup_vowels[1<<CHAR_BIT] = { 0 };) and then
I set to 1 only in 5 places: at the position of the vowels using their
ASCII value as index.
So instead of doing the repeating task if checking
// where c is a char
if(c == 'a' || c == 'A')
a=1;
}
for all vowels, I just can do
int idx = tolower(c);
if(lookup_vowels[idx])
{
// c is a vowel
}
The present variable function similar to the lookup table, here I use the
ASCII code of a vowel as index and set it to 1 if a vowel is present in word.
After scanning all characters in word, I sum all values stored in present.
If the value is greater than 2, then the word has at least 3 or more distinct
vowels and the counter variable is increased.
The function strtok is used to split the line using a defined set of
delimiters, in this case the empty character, tab, carriage return and line
feed. To start parsing the line, strtok must be called with the source string
as the first argument and the delimiters as the second argument. All other
subsequent calls must pass NULL as the first argument. The function returns a
pointer to the next word and returns NULL when no more words have been found.
When a word is found, it calculates the number of distinct vowels and checks if
this number is greater than 2.
fotenotes
1CHAR_BIT defined in limits.h returns the number of bits of byte.
Usually a byte is 8-bit wide, so I could have written 256 instead. But there are
"exotic" architectures where a byte is not 8-bit long, so by doing 1<<CHAR_BIT
I'm getting the correct dimension.

C programming - How to count number of repetition of letter in a word

I am trying to write a program where its supposed to count the occurrence of each characters using getchar(). For example if the input is xxxyyyzzzz the output should be 334 (x is repeated 3 times, y 3 and z 4times). If the input is xxyxx the output should be 212.
Here's what I have tried so far:
double nc ;
for (nc=0 ; getchar()!=EOF;++nc);
printf ("%.0f\n",nc);
return 0;
Input aaabbbccc, output 10. The expected out put is 333
Unfortunately , this shows the total number of characters including enter but not the repetition.
What you are describing is classically handled using a frequency array that is simply an array with the number of elements equal to the number of items you want to count the frequency of (e.g. 26 for all lowercase characters). The array is initialized to all zero. You then map each member of the set you want the frequency of to an array index. (for lowercase characters this is particularly easy since you can map ch - 'a' to map each character a to z to array index 0 - 25 (e.g. 'a' - 'a' = 0, 'b' - 'a' = 1, etc...
Then it is just a matter of looping over all characters on stdin checking if they are one in the set you want the frequency of (e.g. a lowercase character) and incrementing the array index that corresponds to that character. For example if you read all characters with c = getchar();, then you would test and increment as follows:
#define NLOWER 26 /* if you need a constant, define one */
...
int c, /* var to hold each char */
charfreq[NLOWER] = {0}; /* frequency array */
...
if ('a' <= c && c <= 'z') /* is it a lowercase char ? */
charfreq[c - 'a']++; /* update frequency array */
When your are done reading characters, then the frequency of each character is captured in your frequency array (e.g. charfreq[0] holds the number of 'a', charfreq[1] holds the number of 'b', etc...)
Putting it altogether you could do something similar to:
#include <stdio.h>
#define NLOWER 26 /* if you need a constant, define one */
int main (void) {
int c, /* var to hold each char */
charfreq[NLOWER] = {0}, /* frequency array */
i; /* loop var i */
while ((c = getchar()) != EOF ) /* loop over each char */
if ('a' <= c && c <= 'z') /* is it a lowercase char ? */
charfreq[c - 'a']++; /* update frequency array */
/* output results */
printf ("\ncharacter frequency is:\n");
for (i = 0; i < NLOWER; i++)
if (charfreq[i])
printf (" %c : %2d\n", 'a' + i, charfreq[i]);
return 0;
}
Example Use/Output
$ echo "my dog has xxxyyyzzzz fleas" | ./bin/freqofcharstdin
character frequency is:
a : 2
d : 1
e : 1
f : 1
g : 1
h : 1
l : 1
m : 1
o : 1
s : 2
x : 3
y : 4
z : 4
Look things over and let me know if you have questions. This is a fundamental frequency tracking scheme you will use over and over again in many different circumstances.
Outputting All Frequencies Sequentially
With changes to the printf only, you can output the frequencies as a string of integers instead of nicely formatted tabular output, e.g.
#include <stdio.h>
#define NLOWER 26 /* if you need a constant, define one */
int main (void) {
int c, /* var to hold each char */
charfreq[NLOWER] = {0}, /* frequency array */
i; /* loop var i */
while ((c = getchar()) != EOF ) /* loop over each char */
if ('a' <= c && c <= 'z') /* is it a lowercase char ? */
charfreq[c - 'a']++; /* update frequency array */
/* output results */
printf ("\ncharacter frequency is:\n");
for (i = 0; i < NLOWER; i++)
if (charfreq[i])
#ifdef SEQUENTIALOUT
printf ("%d", charfreq[i]);
putchar ('\n');
#else
printf (" %c : %2d\n", 'a' + i, charfreq[i]);
#endif
return 0;
}
Compile with SEQUENTIALOUT defined
$ gcc -Wall -Wextra -pedantic -std=gnu11 -Ofast -DSEQUENTIALOUT \
-o bin/freqofcharstdin freqofcharstdin.c
Example Use/Output
$ echo "my dog has xxxyyyzzzz fleas" | ./bin/freqofcharstdin
character frequency is:
2111111112344
or for the exact string and output in question:
$ echo "xxxyyyzzzz" | ./bin/freqofcharstdin
character frequency is:
334
Sequential Duplicate Characters
If I misunderstood your question and you do not want the frequency of occurrence, but instead you want the count of sequential duplicate characters, then you can do something simple like the following:
#include <stdio.h>
#define NLOWER 26 /* if you need a constant, define one */
int main (void) {
int c, /* var to hold each char */
prev = 0, /* var to hold previous char */
count = 1; /* sequential count */
while ((c = getchar()) != EOF) { /* loop over each char */
if (prev) { /* does prev contain a char ? */
if (prev == c) /* same as last ? */
count++; /* imcrement count */
else { /* chars differ */
printf ("%d", count); /* output count */
count = 1; /* reset count */
}
}
prev = c; /* save c as prev */
}
putchar ('\n');
return 0;
}
Example Use/Output
$ echo "xxyxx" | ./bin/sequentialduplicates
212
$ echo "xxxyyyzzzz" | ./bin/sequentialduplicates
334
you are missing two things, saving the last reviewed char and resetting the counter when updating it...
int c, prev = -1, count = 0;
while ((c = getchar()) != '\n') {
/* if the current char is the same as the last one reviewed increment counter*/
if (c == prev) {
count++;
}
else {
/* handle the start condition of prev */
if (prev != -1) {
printf("%d", count);
}
/* update the prev char reviewed */
prev = c;
/* reset the counter - upon char change */
count = 1;
}
}
/* print out count for final letter */
printf("%d", count);

How would I scan over a string with someone's name and create initials for it?

So basically I need to get an input from a user (user's name) and then iterate over the user's name and create a new string of the initials. Then I have to output the initials. I'm pretty confused on how to do this in C.
One thing to keep in mind is that I have to use pointers. I can't use array operations as stated in my homework instructions. I think I'm on the right path, but I'm confused on how to concatenate characters to a string. Can someone help me out here?
Here is the code so far:
#include <stdio.h>
#include <string.h>
int main() {
char *name = malloc(100);
char c[5];
char *initials = c;
printf("Enter your full name in the following format 'First Middle Last':\n");
fgets(name, 100, stdin);
for(int i = 0; i < strlen(name); i++){
if(name[i] == ' '){
strcat(&name[i + 1], initials);
printf("%s", initials);
}
}
}
Thank you!
Example input: Charles Edwin Roberts
Example output: C.E.R
I would scan the input manually, spotting when I find the first letter of a word and copying the upper case version of that to the array of initials, with a following dot; subsequent letters of a word would be ignored; non-letters would mark the end of a word.
Source code (caps.c)
#include <ctype.h>
#include <stdio.h>
int main(void)
{
char name[100];
while (printf("Please enter the name (First Middle Last): ") > 0 &&
fgets(name, sizeof(name), stdin) != 0)
{
char *s = name;
int inword = 0;
unsigned char c;
char initials[20] = "";
char *p = initials;
char *e = initials + sizeof(initials) - 2;
while ((c = (unsigned char)*s++) != '\0')
{
if (!isalpha(c))
inword = 0;
else if (inword == 0)
{
*p++ = toupper(c);
*p++ = '.';
inword = 1;
if (p >= e)
break;
}
}
if (p > initials)
*(p - 1) = '\0'; /* Zap the final dot to meet the spec */
printf("Initials: %s\n", initials);
}
putchar('\n');
return 0;
}
I decline to run the program repeatedly, so I added a simple loop. Since printf() returns the number of characters it prints, the > 0 test is safe. If you're concerned about your I/O package not flushing standard output before reading from standard input, you could add && fflush(stdout) == 0 to the loop conditions.
Sample run
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -Werror caps.c -o caps
$ caps
Please enter the name (First Middle Last): Charles Edwin Roberts
Initials: C.E.R
Please enter the name (First Middle Last): john michael doe
Initials: J.M.D
Please enter the name (First Middle Last): john.m.doe
Initials: J.M.D
Please enter the name (First Middle Last): the artist formerly known as "prince"
Initials: T.A.F.K.A.P
Please enter the name (First Middle Last): he who has far too many words in his name was here
Initials: H.W.H.F.T.M.W.I.H
Please enter the name (First Middle Last): antiquated (old) man!!!
Initials: A.O.M
Please enter the name (First Middle Last): ajaykumar
Initials: A
Please enter the name (First Middle Last): #(#)$!!!
Initials:
Please enter the name (First Middle Last):
$
There are a number of approaches you can take. You can tokenize the input with functions like strtok and strsep, you can find each space with functions like strchr, or, probably the most efficient for short input like names is simply to loop over your input using a pointer and find all instances where you have a ' ' followed by a letter. To place a '.' in between each initial, simply add a '.' before each subsequent initial after the first initial has been added.
Since you know your initials will include the first character of the string, you can take the first character as an initial and then loop over the remainder of the input. By looking for a ' ' followed by a letter, you avoid the circumstance where you have multiple spaces separating the parts of the name. (if the parts of the name can be tab separated, you can include that as a check as well). You also need to determine a maximum number of characters can make up initials. This just prevents the input of 18 words resulting in an 18 character set of initials.
You can either pass the maximum number as a parameter or you can use a global constant or #define. Note: when adding a '.' in between the initials your space required for the initials will be n (initials) + n - 1 ('.'s) or simply 2 * n - 1. Adding the nul-terminating character, that equates to 2 * n characters total storage in your initials array.
A simple approach could be something like the following:
enum { MAXI = 7, MAXC = 128 }; /* constants for max initials and chars */
...
char *initials (char *intls, char *s)
{
int n = 0;
if (!intls || !s)
return NULL;
if (isalpha(*s))
intls[n++] = toupper(*s); /* add first initial */
for (; n < MAXI && *s && *(s + 1); s++)
if (*s == ' ' && isalpha(*(s + 1))) {
intls[n++] = '.'; /* add . separator before */
intls[n++] = toupper (*(s + 1)); /* each remaining initial */
}
return intls;
}
You can use it in a number of ways. One simple example that limits the number of initials accepted to 4 by setting the constant limit to 2 * n - 1 (e.g. the constant created by enum { MAXI = 7...) would be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
enum { MAXI = 7, MAXC = 128 }; /* constants for max initials and chars */
char *initials (char *intls, char *s);
int main (void) {
char intls[MAXI + 1] = {0};
char name[MAXC] = {0};
size_t len = 0;
printf("Enter your full name ('First Middle Last'): ");
if (!fgets (name, MAXC, stdin)) {
fprintf (stderr, "error: invalid name\n");
return 1;
}
len = strlen (name); /* get length and validate min */
if (len > 1) /* of 2 chars to acount for '\n' */
name[len - 1] = 0; /* remove '\n' */
else {
fprintf (stderr, "error: empty string for name.\n");
return 1;
}
if (!initials (intls, name)) return 1;
printf ("\n name : %s\n initials : %s\n\n", name, intls);
return 0;
}
char *initials (char *intls, char *s)
{
int n = 0;
if (!intls || !s)
return NULL;
if (isalpha(*s))
intls[n++] = toupper(*s);
for (; n < MAXI && *s && *(s + 1); s++)
if (*s == ' ' && isalpha(*(s + 1))) {
intls[n++] = '.';
intls[n++] = toupper (*(s + 1));
}
return intls;
}
Example Output
$ ./bin/initials
Enter your full name ('First Middle Last'): john joe lee
name : john joe lee
initials : J.J.L
$ ./bin/initials
Enter your full name ('First Middle Last'): j
name : j
initials : J
$ ./bin/initials
Enter your full name ('First Middle Last'): john joe lee frank marvin
name : john joe lee frank marvin
initials : J.J.L.F
$ ./bin/initials
Enter your full name ('First Middle Last'): john joe lee bin foo
name : john joe lee bin foo
initials : J.J.L.B
There is no absolute right way to do it. You can make it as robust or as simple as you like as long as it provides the correct initials and avoids errors like reading/writing beyond your array bounds, etc..
According to the definition of strcat()
char *strcat(char *dest, const char *src)
so this line of yours,
strcat(&name[i + 1], initials);
is actually copying the uninitialized contents of the initial[] array into name[i+1]. And I am sure thats not what you want.

Program runs too slowly with large input - C

The goal for this program is for it to count the number of instances that two consecutive letters are identical and print this number for every test case. The input can be up to 1,000,000 characters long (thus the size of the char array to hold the input). The website which has the coding challenge on it, however, states that the program times out at a 2s run-time. My question is, how can this program be optimized to process the data faster? Does the issue stem from the large char array?
Also: I get a compiler warning "assignment makes integer from pointer without a cast" for the line str[1000000] = "" What does this mean and how should it be handled instead?
Input:
number of test cases
strings of capital A's and B's
Output:
Number of duplicate letters next to each other for each test case, each on a new line.
Code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main() {
int n, c, a, results[10] = {};
char str[1000000];
scanf("%d", &n);
for (c = 0; c < n; c++) {
str[1000000] = "";
scanf("%s", str);
for (a = 0; a < (strlen(str)-1); a++) {
if (str[a] == str[a+1]) { results[c] += 1; }
}
}
for (c = 0; c < n; c++) {
printf("%d\n", results[c]);
}
return 0;
}
You don't need the line
str[1000000] = "";
scanf() adds a null terminator when it parses the input and writes it to str. This line is also writing beyond the end of the array, since the last element of the array is str[999999].
The reason you're getting the warning is because the type of str[10000000] is char, but the type of a string literal is char*.
To speed up the program, take the call to strlen() out of the loop.
size_t len = strlen(str)-1;
for (a = 0; a < len; a++) {
...
}
str[1000000] = "";
This does not do what you think it does and you're overflowing the buffer which results in undefined behaviour. An indexer's range is from 0 - sizeof(str) EXCLUSIVE. So you either add one to the
1000000 when initializing or use 999999 to access it instead. To get rid of the compiler warning and produce cleaner code use:
str[1000000] = '\0';
Or
str[999999] = '\0';
Depending on what you did to fix it.
As to optimizing, you should look at the assembly and go from there.
count the number of instances that two consecutive letters are identical and print this number for every test case
For efficiency, code needs a new approach as suggeted by #john bollinger & #molbdnilo
void ReportPairs(const char *str, size_t n) {
int previous = EOF;
unsigned long repeat = 0;
for (size_t i=0; i<n; i++) {
int ch = (unsigned char) str[i];
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
}
char *testcase1 = "test1122a33";
ReportPairs(testcase1, strlen(testcase1));
or directly from input and "each test case, each on a new line."
int ReportPairs2(FILE *inf) {
int previous = EOF;
unsigned long repeat = 0;
int ch;
for ((ch = fgetc(inf)) != '\n') {
if (ch == EOF) return ch;
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
return ch;
}
while (ReportPairs2(stdin) != EOF);
Unclear how OP wants to count "AAAA" as 2 or 3. This code counts it as 3.
One way to dramatically improve the run-time for your code is to limit the number of times you read from stdin. (basically process input in bigger chunks). You can do this a number of way, but probably one of the most efficient would be with fread. Even reading in 8-byte chunks can provide a big improvement over reading a character at a time. One example of such an implementation considering capital letters [A-Z] only would be:
#include <stdio.h>
#define RSIZE 8
int main (void) {
char qword[RSIZE] = {0};
char last = 0;
size_t i = 0;
size_t nchr = 0;
size_t dcount = 0;
/* read up to 8-bytes at a time */
while ((nchr = fread (qword, sizeof *qword, RSIZE, stdin)))
{ /* compare each byte to byte before */
for (i = 1; i < nchr && qword[i] && qword[i] != '\n'; i++)
{ /* if not [A-Z] continue, else compare */
if (qword[i-1] < 'A' || qword[i-1] > 'Z') continue;
if (i == 1 && last == qword[i-1]) dcount++;
if (qword[i-1] == qword[i]) dcount++;
}
last = qword[i-1]; /* save last for comparison w/next */
}
printf ("\n sequential duplicated characters [A-Z] : %zu\n\n",
dcount);
return 0;
}
Output/Time with 868789 chars
$ time ./bin/find_dup_digits <dat/d434839c-d-input-d4340a6.txt
sequential duplicated characters [A-Z] : 434893
real 0m0.024s
user 0m0.017s
sys 0m0.005s
Note: the string was actually a string of '0's and '1's run with a modified test of if (qword[i-1] < '0' || qword[i-1] > '9') continue; rather than the test for [A-Z]...continue, but your results with 'A's and 'B's should be virtually identical. 1000000 would still be significantly under .1 seconds. You can play with the RSIZE value to see if there is any benefit to reading a larger (suggested 'power of 2') size of characters. (note: this counts AAAA as 3) Hope this helps.

Resources