When I compile this program with gcc:
#include <stdio.h>
/* This program accepts some text as an input and gives the output
* of longest word and shortest word lengths*/
int main(){
int c, i, wordcount, symbolcount, longestword, shortestword;
wordcount = symbolcount = longestword = shortestword = 0;
int wlength[1];
while((c = getchar()) != EOF){
++symbolcount;
if(c == ' ' || c == '\n' || c == '\t'){
++wordcount;
wlength[wordcount];
wlength[wordcount - 1] = symbolcount;
symbolcount = 0;
}
}
for(i = 0;i <= wordcount;)
wlength[0] = longestword;
wlength[i] = shortestword;
while(shortestword < 1){
if(shortestword == longestword){
continue;
++i;
}else if(shortestword < longestword && shortestword > 0){
shortestword = wlength[i];
break;
}
}
for(i = 0; i <= wordcount - 1; ++i){
if(wlength[i] > longestword){
longestword = wlength[i];
}else if(wlength[i] < longestword && wlength[i] > shortestword){
continue;
}else{
wlength[i] = shortestword;
}
}
printf("%d\t%d", longestword, shortestword);
return 0;
}
There are no errors or warnings. But when I try to run it, it accepts the input, but there is no output at all. Even when I press Ctrl + D(I work on a debian based distro), current terminal session is not suspended and the program just keeps running. What can be the problem?
The problem is that
int wlength[1];
only declares an array with one element, but you access out of bounds with
shortestword = wlength[i];
This is undefined behavior in C parlance, anything can happen, including what you observe.
To fix this, declare the array with as many elements as you expect i to be.
Make sure your loops over i only take values that do not exceed the array element count.
You have declared an integer array wlength whose size is 2 i.e.,
int wlength[1];
and within the if condition you increment wordcount.
Now assume you have 4 words in a line and wordcount keep on increasing and will be assigned to wlength index but as you have defined the array size 2 , where it overflows. Thus when that is used further in
shortestword = wlength[i];
and
longestword = wlength[i];
it causes junk values to be assigned.
There are several things wrong with your program.
You don't allocate space for only one word.
You have an infinite loop over i. That's acutally why you don't see any output: The program is stuck in this loop.
The second while loop doesn't look as if you knew what you were doing there. I doubt that the condition shortestword < 1 will ever be true. A contunue before other statements makes those statements useless. And what exactly is i here. (Okay, perhaps the while is supposed to be inside the for loop? If so, you need curly braces on the loop body.)
Most of the errors stem from a misunderstanding of the problem. You do not need to store the lengths of all words in order to find the sortest and longest words. Just keeping track of the length of the current word is enough. The algorithm goes like this:
set longest to 0.
set shortest to a large number.
set length to 0.
for each character in the input:
if it is a white-space character:
update longest and shortest if necessary.
reset length to 0.
otherwise:
increase length
This lets you find the longest word in Moby-Dick without having to store more than the current word length. in C, it may look like this:
#include <stdio.h>
int main(void)
{
int longest = 0; // length of currently longest word
int shortest = 0; // length of currently shortest word
int length = 0; // length of current word
int c = getchar();
while (c != EOF) {
if (c == ' ' || c == '\n' || c == '\t') {
if (length) {
if (longest == 0 || length < shortest) shortest = length;
if (length > longest) longest = length;
length = 0;
}
} else {
length++;
}
c = getchar();
}
printf("max: %d\nmin: %d\n", longest, shortest);
return 0;
}
Related
I tried to implement a solution for the exercise on the C language of K&R's book. I wanted to ask here if this could be considered a legal "solution", just modifying the main without changing things inside external functions.
Revise the main routine of the longest-line program so it will
correctly print the length of arbitrary long input lines, and as much
as possible of the text.
#include <stdio.h>
#define MAXLINE 2 ////
int get_line1(char s[], int lim)
{
int c, i;
for (i = 0; i < lim - 1 && ((c = getchar()) != EOF) && c != '\n'; i++) {
s[i] = c;
}
if (c == '\n') {
s[i] = c;
i++;
}
s[i] = '\0';
return i;
}
int main()
{
int len;
int max = MAXLINE;
char line[MAXLINE];
int tot = 0;
int text_l = 0;
while ((len = get_line1(line, max)) > 0) {
if (line[len - 1] != '\n') {
tot = tot + len;
}
if (line[1] == '\n' || line[0] == '\n') {
printf("%d\n", tot + 1);
text_l = text_l + (tot + 1);
tot = 0;
}
}
printf("%d\n", text_l);
}
The idea is to set the max lenght of the string considered for the array line ad 2.
For a string as abcdef\n , the array line will be ab. Since the last element of the array is not \n (thus the line we are considering is not over), we save the length up until now and repeat the cycle. We will get then the array made of cd, then ef and at the end we will get the array of just \n. Then the else if condition is executed, since the first element of this array is\n, and we print the tot length obtained from the previous additions. We add +1 in order to also consider the new character \n. This works also for odd strings: with abcdefg\n the process will go on up until we reach g\n and the sum is done correctly.
Outside the loop then we print the total amount of text.
Is this a correct way to do the exercise?
The exercise says to “Revise the main routine,” but you altered the definition of MAXLINE, which is outside of main, so that is not a valid solution.
Also, your code does not have the copy or getline routines of the original. Your get_line1 appears to be identical except for the name. However, a correction solution would use identical source code except for the code inside main.
Additionally, the exercise says to print “as much as possible of the text.” That is unclearly stated, but I expect it means to keep a buffer of MAXLINE characters (with MAXLINE at its original value of 1000) and use it to print the first MAXLINE−1 characters of the longest line.
Hello i got an assignment in c programing and i dont really understand the c/malloc function i think,
they told us that we need to do the free function after using this function, but every time i do free it breaks the program
The assignment is :
collect an input string.
every upper case letter to lower
every lower case letter to upper
if there is number do series of numbers from '9' until the input number but with out it (for '6' do '9','8','7'. (with out 6))
if there is other stuff don't add it in to the out put.
input example : A$q6#G4
output example : aQ987g98765
it is not allowed to change the input string.
in the input allowed to be every thing.
the output sting needs to be exactly in the array size
(if 123 = the size of will be input[2])
photo of the error
the error : wntdll.pdb contains the debug information required to find the source for the module ntdll.dll
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
/* Function declarations */
char Ex1FNumbers(char);
char Ex1FLetters(char);
/* ------------------------------- */
//
int main()
{
system("cls"); //delete when send
int select = 0, i, all_Ex_in_loop = 0;
printf("Run menu once or cyclically?\n(Once - enter 0, cyclically - enter other number) ");
if (scanf_s("%d", &all_Ex_in_loop) == 1)
do
{
for (i = 1; i <= 3; i++)
printf("Ex%d--->%d\n", i, i);
printf("EXIT-->0\n");
do {
select = 0;
printf("please select 0-3 : ");
scanf_s("%d", &select);
} while ((select < 0) || (select > 3));
switch (select)
{
case 1: //Ex1
{
int size, i, n, counter = 0;
char inPut[] = "";
char outPut[] = "";
char* Ptr_inPut_address, * Ptr_outPut_address, num;
printf("Please enter a string :\n");
scanf("%s", inPut);
size = strlen(inPut);
Ptr_outPut_address = &outPut;
Ptr_inPut_address = (char*) calloc(size+1 , sizeof(char));
Ptr_outPut_address = (char*) calloc(0, sizeof(char));
if (!Ptr_inPut_address || !Ptr_outPut_address)
{
break;
}
for (i = 0; i < size; i++)
{
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
if (inPut[i] >= 'a' && inPut[i] <= 'z' || inPut[i] >= 'A' && inPut[i] <= 'Z')
{
if (inPut[i] >= 'a' && inPut[i] <= 'z')
{
outPut[counter++] = inPut[i] - 32;
}
else
{
outPut[counter++] = inPut[i] + 32;
}
}
else if (inPut[i] <= '9' && inPut[i] >= '0')
{
n = '9' - inPut[i];
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter + n);
for (n; n > 0; n--) // "o" of 8 and not "n" because 8 is the max num for this "for".
{
outPut[counter++] = inPut[i] + n;
}
}
}
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
outPut[counter] = '\0';
Ptr_outPut_address = &outPut;
printf("%s\n", Ptr_outPut_address);
if (Ptr_outPut_address != NULL)
{
free(Ptr_outPut_address);
}
if (Ptr_inPut_address != NULL)
{
free(Ptr_inPut_address);
}
} break;
case 2: //Ex2
{
}break;
case 3: //Ex3
{
}break;
}
} while (all_Ex_in_loop && select);
system("pause");//delete when send
main();//delete when send
//return 0; // return when send
}
char inPut[] = "";
char outPut[] = "";
This declares two arrays that contain exactly one char, initializing them to '\0'. That's what the above means in C. This does not mean that these two arrays will have infinite size and can store any string. That's not how this works. But then, immediately afterwards:
scanf("%s", inPut);
This is guaranteed to overflow the array, since it is capable of holding only one char. Any string this reads will have at least two chars: the single read character, followed by '\0'. This results in memory corruption and undefined behavior.
There are several other bugs in the shown code. One more example:
Ptr_outPut_address = &outPut;
This has the effect of setting this variable to the starting address of a char array that was declared earlier.
Ptr_outPut_address = (char*)realloc(Ptr_outPut_address, counter);
You can only realloc something that was malloced, realloced, or calloced. No exceptions. You cannot realloc anything else. The char array was not malloced, realloced, or calloced. C does not work this way.
Several other problems exists in the shown code. Looks like this entire program was written all at once, before an attempt was made to test everything. This approach is very unlikely to succeed, and will likely produce many different kinds of bugs, such as the one that I've described. This makes it difficult to analyze and fix everything, since you're not looking for just one bug, but an unknown number of bugs. Plus it is likely that there will be an eventual realization that some or most of what was written need to be rewritten from scratch since the shown approach turned out to be fundamentally wrong.
Which is what you should probably do: start from scratch, write only a few lines of code, before testing them, and making sure that they work correctly before proceeding to write more code. If you attempt to fix just the problems that I explained it's likely that this will just create other problems, additionally, there are other problems as well, I just didn't mention them. The entire approach that was used here needs to be changed, fundamentally.
I am new to C and working on homework. I got most work done, but I can't pass all test cases that used by my professor. He refuses to post cases being used in the auto grader.
What would be the cases that I have missed?
Any clue will be appreciated!!!
Write a program to remove all the blank characters (space and tab) in a line.
Then, count the number of appearances of each character.
Input
A single line with a maximum length of 5000 characters
Output
First line: The line after removing all the blank characters.
If the line is empty, don’t print anything in the output.
Next lines: Each line contains a character that appears in the line and its count.
Note that, the characters must appear according to their ASCII number order. (http://www.asciitable.com)
#include <stdio.h>
int main (){
int c = 0;
int characters[128] = {0}; // subscripts for the ASCII table
int count = 0; // number of characters been reading in
while(count < 5001 && (c = getchar()) != EOF) {
// 9 -> TAB on ASCII, 32 -> Space on ASCII
if (c != 9 && c != 32) {
putchar(c);
characters[c]++;
count++;
}
}
fflush(stdout);
printf("\n");
for (int i = 0; i < 128; i++) {
if (characters[i] != 0) {
printf("%c %d\n", i, characters[i]);
}
}
return 0;
}
Again, any help will be really appreciated!
Update:
The code has been corrected.
Probably you don't want to write
characters[index] = 0;
What you want instead is probably
text[index] = 0;
This is my first post on stack overflow :)
I didn't find relevant post to my issue despite numerous posts on "counting words".
I started C 2 weeks ago. I have to return the number of words in a string, this is part of a larger exercise I m working on at the moment. I can't figure out why it doesn't work and I am kindly asking for some tips here.
ft_strlen(char *str) //counting nb of char in the string
{
int size;
size = 0;
while (str[size])
size++;
return (size);
}
int ft_word_count(char *str)
{
int i;
int size;
int count_word;
i = 0;
size = ft_strlen(str);
count_word = 0;
while (str[i] < size - 1) //counting nb of words in the string, I added "-1" to size to get rid of the '\0'
{
if (i <= 32 || i > 126 ) //defining what will make a word
count_word++;
i++;
}
return (count_word);
}
int main(void)
{
char str[]="Meine Frau liebt grosse Pferde";
ft_strlen(str);
printf("%d", ft_word_count(str));
return (0);
}
it returns 0 instead of 5, strangely, don't figure out why.
If I just use my strlen, it returns "30", as expected. So something is wrong with ft_word_count
Compiled with gcc.
Syntax is not concise but is part of the norm asked by my school.
thanks for your input
Charles
you should ignore multiple spaces for counting correct
i=0;
count_word=0;
while(str[i]>0)
{
if((str[i]!= ' '))
{
if(!toggle && str[i]!= ' ')
count_word++;
toggle=1;
}
else
toggle=0;
i++;
}
I believe that you meant to use logic more like this:
if(str[i] <= 32 || str[i] > 126) count_word++;
In the code that you posted, you are looking at the value of your index, not the character value in the string.
Even so, this is not why you are receiving "0" as a result. The cause of this is your while condition. You are checking to see if the numeric ASCII value within the string is greater than the length of the string... which I can assure you, it is. Therefore, you also want to change your white to be:
while(i < size - 1)
Personally, I would likely have checked for \n, space and \t instead, but to each his own!
The problem is these lines
while (str[i] < size - 1) // Here you compare the individual chars and
// the length of the string. That makes
// no sense
{
if (i <= 32 || i > 126 ) // Here you compare the index i and some
// fixed numbers. That makes no sense
count_word++;
i++;
}
It seems you have swapped the two, i.e. you use str[i] when you should use i and you use i when you should use str[i]
So if you change your code to:
while (i < size - 1)
{
if (str[i] <= 32 || str[i] > 126 )
count_word++;
i++;
}
You'll see that things start to make more sense. That code will print 4. That is still wrong but now you have some code that you can continue with.
A simple approach could be:
while (i < size - 1)
{
if (str[i] == ' ')
count_word++;
i++;
}
count_word++;
That code will print 5. However, the code is too simple as it count double spaces as words.
In other words - you need to add more code to handle such case but I guess that is part of the learning process. Good luck.
Error Part
while (str[i] < size - 1)
Here its checking with the ascii value at that place of string which will always be false and hence loop is not running.
Correct Way
while (i < size - 1)
{
if (str[i] == ' ')
count_word++;
i++;
}
count_word++;
There are multiple problems in your code:
while (str[i] < size - 1) is incorrect as you are comparing the value f the character to the size of the string instead of the index: it should be while (i < size).
if (i <= 32 || i > 126 ) is incorrect: it is not a proper way to check for word separators as non ASCII characters will not be considered part of the word and the encoding might not be ASCII anyway. You should instead use isspace() from <ctype.h>.
Furthermore, counting the spaces is not a way to count the words. You should instead count the number of transitions from space to non-space.
Here is a simpler version:
#include <ctype.h>
#include <stdio.h>
int ft_word_count(const char *str) {
unsigned char c, last = ' ';
int count = 0;
for (int i = 0; (c = str[i]) != '\0'; i++) {
if (!isspace(c) && isspace(last))
count++;
last = c;
}
return count;
}
int main(void) {
char str[] = "Meine Frau liebt grosse Pferde";
printf("%d\n", ft_word_count(str));
return 0;
}
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.