Why does the program end without 'quit'? - arrays

my code problem:
this code only lets me input one command then jumps out of the loop without even inputting 'quit'
The problem is to parse a series of commands that instruct a robot arm on how to manipulate blocks that lie on a flat table. Initially, there are n blocks on the table (numbered from 0 to n − 1) with block bi adjacent to block bi+1 for all 0 ≤ i < n − 1 as shown in the diagram below:
https://ibb.co/WpWQBYT
The valid commands for the robot arm that manipulates blocks are:
• move a onto b
where a and b are block numbers, puts block a onto block b after returning any blocks that are stacked on top of blocks a and b to their initial positions.
• move a over b
puts block a onto the top of the stack containing block b, after returning any blocks that are stacked on top of block a to their initial positions.
• pile a onto b
moves the pile of blocks consisting of block a, and any blocks that are stacked above block a, onto block b. All blocks on top of block b are moved to their initial positions prior to the pile taking place. The blocks stacked above block a retain their order when moved.
• pile a over b
puts the pile of blocks consisting of block a, and any blocks that are stacked above block a, onto the top of the stack containing block b. The blocks stacked above block a retain their original order when moved.
• quit
terminates manipulations in the block world. Any command in which a = b or in which a and b are in the same stack of blocks is an illegal command. All illegal commands should be ignored and should have no effect on the configuration of blocks.
Input:
https://ibb.co/pWJ9c7Q
Output:
https://ibb.co/Nt03mm3
[I only type the code of the first command for now.]
my code:
#include<stdio.h>
#include<string.h>
int main(){
int noi=0;
printf("please input n:");
int n;
scanf(" %d",&n);
int arr[n][n];
int i,j;
for(i=0;i<n;i++){
for(j=0;j<n;j++){
arr[i][j]=-1;
}
arr[i][0]=i;
}
char str1[5],str2[5];
int s,d;
while(strcmp(str1,"quit")!=0){
s=0;
d=0;
while(!(s>=1&&s<=n&&d>=1&&d<=n)){
scanf(" %s %d %s %d",str1,&s,str2,&d);
}
if(strcmp(str1,"move")==0){
if(strcmp(str2,"onto"==0)){
//empty s
for(i=0;i<n&&arr[s][i]!=-1;i++){
arr[arr[s][i]][0]=arr[s][i];
arr[s][i]=-1;
}
//empty d
for(i=0;i<n&&arr[d][i]!=-1;i++){
arr[arr[d][i]][0]=arr[d][i];
arr[d][i]=-1;
}
//now move s to d
i=1;
while(arr[d][i]!=-1){
i++;
}
arr[d][i]=arr[s][0];
arr[s][0]=-1;
}else if(strcmp(str2,"over")==0){
}else{
continue;
}
}else if(strcmp(str2,"pile")==0){
}else{
continue;
}
}
//print results
for(i=0;i<n;i++){
printf("%d:",i);
for(j=0;j<n&&arr[i][j]!=-1;j++){
printf("%d ",arr[i][j]);
}
printf("\n");
}
}

There are an extremely large number of errors in your code. Probably the one of the largest impediments to recognizing the problems in your code, while not an error, is the lack of spacing in your code. Scrunching everything together makes your code very difficult to read (especially for older eyes). Open the spacing on your code up a bit.
As mentioned in the comments above, your first show-stopping problem is the use of str1 while it is uninitialized. That invokes undefined behavior and then defined operation of your code is over at that point. Your code could SegFault, appear to work normally, or anything in between.
When you are taking user-input, it is recommended you use a line-oriented input function like fgets() or POSIX getline(). That one change avoids a large number of pitfalls associated with attempting to take user-input with a formatted-input function like scanf(). If you don't know what each of the pitfalls are associated with its use -- don't use it for user input. 9 out of 10 of the user input questions on this site relate to the misuse of scanf().
Additionally, Don't Skimp On Buffer Size!!. What happens if the user enters "iskabibble" instead of "quit"? How are the characters that do not fit in str1[5] handled? What if the cat steps on the keyboard and 100 characters are entered? It's better to be 10,000 characters too long, than one character too short. Take input with fgets() and a sufficiently sized buffer, and then parse the values needed with sscanf() instead of trying to do both with scanf(), e.g.
#define MAXC 512 /* if you need a constant, #define one (or more)
* ( don't skimp on buffer size!! )
*/
int main (void) {
char buf[MAXC]; /* buffer to store all user input */
int n; /* if possible, declare variables at beginning of scope */
fputs ("please input n for (n x n array): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read all user input with fgets() */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &n) != 1) { /* validate every conversion */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
Reusing a single buffer to handle user-input simplifies things greatly.
While it is fine to use a VLA (Variable Length Array) for practice problems, be aware compilers are not required to support them from C11 forward, and none were supported before C99. Better to dynamically allocate for production code.
Your variable declarations should be in the scope where the variables are needed. This helps prevent variable shadowing of common variables such as i, j, etc.. 300 lines down in your code. For example, loop variables can be declared as part of the loop declaration so long as they are not needed outside the loop, e.g.
int arr[n][n]; /* VLA's are an 'optional' feature since C11 */
for (int i = 0; i < n; i++) { /* i, j can be decalred with loop scope */
for (int j = 0; j < n; j++) {
arr[i][j] = -1;
}
arr[i][0] = i;
}
When you need to user to enter specific input, better to loop continually until the user provides valid input (respecting their ability to cancel input by generating a manual EOF with Ctrl + d, or Ctrl + z on windows). For example where str1, s, str2 and d are needed:
while (strcmp (buf, "quit")) { /* loop until quit */
char str1[MAXC] = "", str2[MAXC] = "";
int s = 0, d = 0;
while (1) { /* loop continually */
fputs ("enter str1 s str2 d: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read / validate input */
puts ("(user canceled input)");
return 0;
}
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from end of buf */
if (strcmp (buf, "quit") == 0) /* if "quit", break */
break;
/* parse and validate separate values, always protect array bounds */
if (sscanf (buf, "%511s %d %511s %d", str1, &s, str2, &d) != 4) {
fputs (" error: invalid format or integer input.\n", stderr);
continue;
} /* validate range of integers (negated conditions are confusing) */
if ((0 <= s && s < n) && (0 <= d && d < n))
break; /* exit loop on good input */
else /* otherwise, handle error */
fputs (" error: value for s or d out of range.\n", stderr);
}
(note: fgets() reads and includes the '\n' generated by the user pressing Enter, so before comparing for "quit" you will need to remove the newline with strcspn())
Since str1 and str2 are parsed from buf using sscanf() there is no '\n' to remove. However, when using sscanf() you must use the field-width modifier to protect the array bounds from overrun -- otherwise the use of scanf() or sscanf() to fill the character array is no safer than gets(), see: Why gets() is so dangerous it should never be used!
if (strcmp (str1, "move") == 0) { /* handle move */
if (strcmp (str2, "onto") == 0) { /* onto? */
int i; /* declare i in scope needed */
// empty s
for (i = 0; i < n && arr[s][i] != -1; i++) {
arr[arr[s][i]][0] = arr[s][i];
arr[s][i] = -1;
}
// empty d
for (i = 0; i < n && arr[d][i] != -1; i++){
arr[arr[d][i]][0] = arr[d][i];
arr[d][i] = -1;
}
// now move s to d
i = 1;
while (arr[d][i] != -1) {
i++;
}
arr[d][i] = arr[s][0];
arr[s][0] = -1;
}
else if (strcmp (str2, "over") == 0) {
(void)str2; /* no-op prevents empty scope */
}
else {
continue;
}
}
else if (strcmp (str2, "pile") == 0) {
(void)str2;
}
else {
continue;
}
(note: the final else is not needed)
Complete Code
While I am still unclear on what your logic is supposed to do, handing the input can be done as shown above. Fixing the logic is left to you.
#include <stdio.h>
#include <string.h>
#define MAXC 512 /* if you need a constant, #define one (or more)
* ( don't skimp on buffer size!! )
*/
int main (void) {
char buf[MAXC]; /* buffer to store all user input */
int n; /* if possible, declare variables at beginning of scope */
fputs ("please input n for (n x n array): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read all user input with fgets() */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &n) != 1) { /* validate every conversion */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
int arr[n][n]; /* VLA's are an 'optional' feature since C11 */
for (int i = 0; i < n; i++) { /* i, j can be decalred with loop scope */
for (int j = 0; j < n; j++) {
arr[i][j] = -1;
}
arr[i][0] = i;
}
while (strcmp (buf, "quit")) { /* loop until quit */
char str1[MAXC] = "", str2[MAXC] = "";
int s = 0, d = 0;
while (1) { /* loop continually */
fputs ("enter str1 s str2 d: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read / validate input */
puts ("(user canceled input)");
return 0;
}
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from end of buf */
if (strcmp (buf, "quit") == 0) /* if "quit", break */
break;
/* parse and validate separate values, always protect array bounds */
if (sscanf (buf, "%511s %d %511s %d", str1, &s, str2, &d) != 4) {
fputs (" error: invalid format or integer input.\n", stderr);
continue;
} /* validate range of integers (negated conditions are confusing) */
if ((0 <= s && s < n) && (0 <= d && d < n))
break; /* exit loop on good input */
else /* otherwise, handle error */
fputs (" error: value for s or d out of range.\n", stderr);
}
if (strcmp (str1, "move") == 0) { /* handle move */
if (strcmp (str2, "onto") == 0) { /* onto? */
int i; /* declare i in scope needed */
// empty s
for (i = 0; i < n && arr[s][i] != -1; i++) {
arr[arr[s][i]][0] = arr[s][i];
arr[s][i] = -1;
}
// empty d
for (i = 0; i < n && arr[d][i] != -1; i++){
arr[arr[d][i]][0] = arr[d][i];
arr[d][i] = -1;
}
// now move s to d
i = 1;
while (arr[d][i] != -1) {
i++;
}
arr[d][i] = arr[s][0];
arr[s][0] = -1;
}
else if (strcmp (str2, "over") == 0) {
(void)str2; /* no-op prevents empty scope */
}
else {
continue;
}
}
else if (strcmp (str2, "pile") == 0) {
(void)str2;
}
else {
continue;
}
}
// print results
for (int i = 0; i < n; i++) {
printf ("%d:\n", i);
for (int j = 0; j < n; j++) {
if (arr[i][j] != -1)
printf (" % 3d", arr[i][j]);
else
fputs (" [ ]", stdout);
}
putchar ('\n');
}
}
Example Use/Output
With intentional errors in input:
$ ./bin/vla_quit
please input n for (n x n array): 5
enter str1 s str2 d: move 2 onto 3
enter str1 s str2 d: move bananas onto gorillas
error: invalid format or integer input.
enter str1 s str2 d: move 1 onto 4
enter str1 s str2 d: move -1 onto 2
error: value for s or d out of range.
enter str1 s str2 d: move 0 onto 2
enter str1 s str2 d: quit
0:
[ ] [ ] [ ] [ ] [ ]
1:
[ ] [ ] [ ] [ ] [ ]
2:
[ ] [ ] [ ] [ ] [ ]
3:
[ ] [ ] [ ] [ ] [ ]
4:
[ ] [ ] [ ] [ ] [ ]
Always compile with warnings-enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic to your gcc/clang compile string (also consider adding -Wshadow to warn on shadowed variables). For VS (cl.exe on windows), use /W3. All other compilers will have similar options. Read and understand each warning -- then go fix it. They will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you.
Let me know if you have further questions.

Related

How can I get an input text to be printed in reverse, with a specified length to each output line

I need to write a code that takes a user input and outputs a mirror image of the input, with a line break every x characters, where x is a number input by the user.
For example:
Enter the length of the output line: 5
Enter your text (control-d to exit): Hello there
output:
olleH
.ereh
....t
12345
(the dots are spaces)
My current code is:
int main() {
char input[121], mirror[121];
int length = 0, i, str = 0, j = 2, reset = 0, k;
printf("Enter the width of an output line:\n");
scanf("%d", &length);
printf("Enter your text (control-d to exit):\n");
while (scanf("%c", &input[i]) == 1) {
i++;
}
reset = length;
str = strlen(input);
printf("\n");
input[str] = '\0';
for (i = 0; i < ((str / length) + 1); i++) {
for (k = 0; k < length; k++) {
if ((length - k) >= 0) {
mirror[k] = input[reset - k];
printf("%c", mirror[i]);
}
else if ((length - k) < 0) {
printf("\n");
mirror[k] = input[reset + 1];
reset = (j * reset);
j++;
}
}
}
return 0;
}
My output is nowhere near correct, and although I've had previous attempts that were closer to correct, overall I am not sure how to really approach this problem.
Your algorithm went off the rails, but we can get the train back up on the track. The key is to recognize you have to reverse each WORD in the string preserving whitespace and then output characters in groups of width characters at a time from your output array. If the final group has output that would be beyond the length of the string, output spaces and then the final characters.
That and cleaning up your code, validating all input and getting rid of scanf() that is full of pitfalls for the new C programmer, you could do the following:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 128 /* if you need a constant, #define one (or more) */
int main (void) {
char input[MAXC],
output[MAXC];
size_t length = 0, /* strlen returns size_t not int */
width = 0,
i = 0;
/* just reuuse the 'input' buffer to read width and use sscanf() for
* the conversion VALIDATING both by CHECKING THE RETURN.
*/
fputs ("Enter the width of an output line : ", stdout);
if (!fgets (input, MAXC, stdin) || sscanf (input, "%zu", &width) != 1) {
puts ("(user canceled input) or invalid integer input");
return 1;
}
/* read/validate input, trim \n and get length */
fputs ("Enter your text : ", stdout);
if (!fgets (input, MAXC, stdin)) {
puts ("(user canceled input)");
return 1;
}
input[strcspn (input, "\n")] = 0; /* trim \n from end */
length = strlen (input); /* get input length */
putchar ('\n'); /* use putchar() to output a single-character */
/* reverse words in input preserving whitespace */
for (i = 0; input[i];) { /* loop each char in input */
if (isspace (input[i])) { /* if space, copy to output */
output[i] = input[i];
i++; /* increment loop index */
}
else { /* otherwise, not space, get length of word, save index */
size_t wordlen = strcspn (input + i, " \t\n"),
index = i;
if (!wordlen) { /* if wordlen is zero, at end, break */
break;
}
while (wordlen--) { /* reverse loop wordlen filling output */
output[i++] = input[index + wordlen]; /* increment loop index */
}
}
}
output[i] = 0; /* nul-terminate output */
for (i = 0; i < length;) { /* loop while i < length */
size_t j = width; /* counter for width characters */
while (j--) { /* loop width number of times */
if (j + i >= length) { /* if i + j outside string put space */
putchar (' ');
}
else { /* otherwise */
putchar (output[i++]); /* output next char in output */
}
}
putchar ('\n'); /* tidy up with newline */
}
}
(note: you can trim the '\n' and save the length in a single call with input[(length = strcspn (input, "\n"))] = 0;, but up to you)
Also note, the last if-else can be considerably shortened using a ternary operator. That would reduce the final comparison to:
putchar (j + i >= length ? ' ' : output[i++]);
Up to you which you want to use.
Example Use/Output
Now your example output is correctly reproduced, e.g.
$ ./bin/revstr
Enter the width of an output line : 5
Enter your text : Hello there
olleH
ereh
t
Or if you added a character, e.g.
$ ./bin/revstr
Enter the width of an output line : 5
Enter your text : Hello Mickey
olleH
yekc
iM
Look things over and let me know if you have further questions.
Relax... take a deep breath... and think slowly about what's needed...
It is a standard 'beginner' notion that things are complicated and involve lots of variables and esoteric calculations. It takes a bit of experience to learn to pause-and-assess the problem BEFORE touching the keyboard.
Consider this:
int main() {
char input[ 120 + 1]; // some variables
int olen = 0;
// some user input
printf( "Width of an output line: " );
scanf( "%d", &olen );
/* omitting validation for brevity */
// more user input using "scanf()" parameters that work
printf( "Enter text: ");
if( scanf( " %120[^\n]", input ) != 1 ) {
fprintf( stderr, "scanf failed\n" );
return 1;
}
// Now, output from the "back end", while counting 'modulo' to insert line breaks.
int up = 1;
for( int i = strlen( input ) - 1; i >= 0; i-- ) {
putchar( input[ i ] );
if( up++ % olen == 0 ) // time for a 'line break' ?
putchar( '\n' );
}
// All done.
return 0;
}
Width of an output line: 5
Enter text: Hello there
ereht
olle
H
Width of an output line: 8
Enter text: Twas brillig and the slithy toves...
...sevot
.yhtils. // some '.' added in post processing for clarity.
eht dna.
gillirb.
sawT...
There's no need for a 2nd buffer, or for a lot of variables whose values you begin to lose track of... Just keep things simple!
An exercise is to modify the code above to reverse the string "in place", then output substrings of the right length on separate lines. Also, reverse each substring independently.

Variable sized arrays and array pointer in C

I am doing coding exercise in HackerRank. In the Variable Sized Arrays exercise of C++, I tried to do it with C, but I couldn't get it right.
My code sometimes pass the simple test case 0, but sometimes not. The code was exactly the same but came out with different answer.
int main() {
/* Enter your code here. Read input from STDIN. Print output to STDOUT */
int n, q;
scanf("%d%d", &n, &q);
int *p_arr[n];
if (n > 0) {
for (int i = 0; i < n; i++) {
int tmp;
scanf("%d", &tmp);
int tmp_arr[tmp];
p_arr[i] = tmp_arr;
for (int j = 0; j < tmp; j++) {
int value;
scanf("%d", &value);
p_arr[i][j] = value;
printf("%d ", p_arr[i][j]);
}
printf("\n");
}
}
if (q > 0) {
for (int i = 0; i < q; i++) {
int row, col;
scanf("%d%d", &row, &col);
printf ("%d %d\n", row, col);
int answer = p_arr[row][col];
printf("%d\n", answer);
}
}
return 0;
}
This was the wrong answer I got
1 5 4
1 2 8 9 3
0 1
21973
1 3
32764
I didn't know where the 21973 and 32764 came from.
This was the answer I expected
1 5 4
1 2 8 9 3
0 1
5
1 3
9
Sometimes I got the wrong answer, sometimes I got the correct. Why was that?
Thanks a lot!
Continuing from the comments, if the exercise involves a Variable Length Array, then it should be written in C because the C++ standard does not provide VLAs (and they have been made optional beginning in C11 after being added with C99)
The primary problem with your code (aside from a complete failure to allocate the inputs) is that it invokes Undefined Behavior because int tmp_arr[tmp] goes out of scope at the end of the for loop. What you assign with p_arr[i] = tmp_arr; no longer exists outside the loop because tmp_arr no longer exists. Any attempt to access a value after it has gone out of scope invokes Undefined Behavior, see Undefined, unspecified and implementation-defined behavior
Every time you handle input, you must validate the input succeeded and the value obtained is within a valid range. For instance both n and q must be positive values. Using scanf you could do (minimally)
if (scanf("%d%d", &n, &q) != 2 || n <= 0 || q <= 0) {
fputs ("error: invalid format or value.\n", stderr);
return 1;
}
The above validates that 2 inputs were received and that both are positive values.
In order to preserve the pointer assigned with p_arr[i] = tmp_arr;, tmp_arr must be an allocated type to handle an unknown tmp number of elements, or it must be declared outside the loop (and large enough to handle all anticipated values -- which given that tmp is read as input -- doesn't seem like the intended approach). You cannot declare tmp_arr static either as there would only be one instance and assigning it repeatedly to p_arr[i] would leave all elements of p_arr[i] pointing to the same place.
Simply allocate for tmp_arr and then the assignment to p_arr[i] will survive for the life of the program or until it is freed, e.g.
int *tmp_arr = calloc (tmp, sizeof *tmp_arr); /* allocate */
if (!tmp_arr) { /* validate every allocation */
perror ("calloc-tmp_arr");
return 1;
}
p_arr[i] = tmp_arr;
(note: calloc was used above to zero the new memory allocated. You can use malloc instead since the code assigns to each element)
Putting it altogether and adding (minimal) validation, you could do something similar to:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
int n, q; /* validate EVERY input */
if (scanf("%d%d", &n, &q) != 2 || n <= 0 || q <= 0) {
fputs ("error: invalid format or value.\n", stderr);
return 1;
}
int *p_arr[n]; /* array of n-pointers to int */
for (int i = 0; i < n; i++) {
int tmp;
if (scanf("%d", &tmp) != 1 || tmp <= 0) { /* validate! */
fputs ("error: invalid input - tmp.\n", stderr);
return 1;
}
int *tmp_arr = calloc (tmp, sizeof *tmp_arr); /* allocate */
if (!tmp_arr) { /* validate every allocation */
perror ("calloc-tmp_arr");
return 1;
}
p_arr[i] = tmp_arr;
for (int j = 0; j < tmp; j++) {
int value;
if (scanf("%d", &value) != 1) { /* validate! */
fputs ("error: invalid input - value.\n", stderr);
return 1;
}
p_arr[i][j] = value;
printf ("%d ", p_arr[i][j]);
}
putchar ('\n'); /* no need to printf a single-character */
}
for (int i = 0; i < q; i++) {
int row, col; /* validate! */
if (scanf ("%d%d", &row, &col) != 2 || row < 0 || col < 0) {
fputs ("error: invalid input, row or col.\n", stderr);
return 1;
}
printf ("%d %d\n%d\n", row, col, p_arr[row][col]);
}
for (int i = 0; i < n; i++)
free (p_arr[i]);
return 0;
}
(Note: untested as no hackerrank input was provided)
Look things over and let me know if you have further questions.

Print out the third word in a string. C

So I am trying to write a program that takes a sentence and prints it out from the third word. Ex one two three four should print out three four.
Now this code works but I have no idea why as the logic under the else statement make it seem like it should not.
Would be thankful if someone could explain why it works like this.
Here is the code:
#include <stdio.h>
#include <string.h>
#define SIZE 100
int main(void) {
char arr[SIZE];
char *point;
char again = 'n';
do {
int count = 0;
for (int i = 0; i < SIZE; i++) {
arr[i] = '\0';
}
printf("Enter a sentence:");
gets(arr);
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
}
if (count < 2) {
printf("The sentence is to short!\n");
} else {
count = 1; //shouldn't this be count = 0?
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
if (count == 2) {
point = &arr[i + 2]; //shouldn't this be [i+1]?
}
}
printf("%s\n", point);
}
printf("Do you want to try again? (y/n)");
scanf("%c", &again);
while (getchar() != '\n');
} while (again == 'y' || again == 'Y');
return 0;
}
Your code has multiple problems:
You should never use gets(). This function has been removed from the C Standard because it cannot be given the maximum number of characters to write to the destination buffer, so any sufficiently long line from the input stream will cause undefined behavior. This is a classic security flaw. Use fgets() instead.
The loop while (getchar() != '\n'); will cause an infinite loop if there is no newline before the end of file, which will happen if you redirect input an empty file. You should also check for EOF:
while ((c = getchar()) != EOF && c != '\n')
continue;
There is no need to initialize the destination array, but you should check if the input operation succeeded, by comparing the return value of fgets() to NULL.
When iterating through the array to count spaces, you should stop at the null terminator. The contents of the array beyond the null terminator is indeterminate after the input operation, even if you initialized it prior to the call.
The code to skip the words is cumbersome and not easy to validate. Indeed point = &arr[i+2]; should be point = &arr[i+1].
words might be separated by more than one space, and initial spaces should be ignored.
Here is a corrected version using string functions strspn and strcspn to skip blanks and non-blanks:
#include <stdio.h>
#include <string.h>
#define SIZE 100
#define WS " \t\n\r\v\f" /* white space characters */
int main(void) {
char arr[SIZE];
char *p;
for (;;) {
printf("Enter a sentence:");
if (fgets(arr, sizeof arr, stdin) == NULL)
break;
p = arr;
p += strspn(p, WS); /* skip initial spaces */
p += strcspn(p, WS); /* skip first word */
p += strspn(p, WS); /* skip spaces */
p += strcspn(p, WS); /* skip second word */
p += strspn(p, WS); /* skip spaces */
if (*p == '\0') {
printf("The sentence is too short!\n");
} else {
printf("%s", p);
}
printf("Do you want to try again? (y/n)");
if (fgets(arr, sizeof arr, stdin) == NULL)
break;
if (*arr != 'y' && *arr != 'Y')
break;
}
return 0;
}
Another simple way to handle the word count is to walk-a-pointer down your string in a state loop keeping track of whether you are in a word (if so increase word count), otherwise you are not in a word and just keep walking down the buffer (i.e. iterating over each char) until you find the next word (or end of string).
The logic is simple, after filling your buffer, and setting a pointer to it, e.g.
#define MAXC 1024 /* buffer size (don't skimp) */
#define NWORD 3 /* output beginning with NWORD word */
...
char buf[MAXC] = "", /* buffer to hold line */
*p = buf; /* pointer to walk down buffer */
int n = 0, /* word counter */
in = 0; /* flag - in a word */
Just loop checking each character with isspace() and handle setting your in flag to either 1 (in word) or 0 (in space before or between words) incrementing your counter each time you go in a new word, and exiting the loop when your count reaches 3, e.g.
for (; *p; p++) { /* loop over each char */
if (!in && !isspace(*p)) { /* if not in word and not space */
in = 1, n++; /* set in flag, increment words */
if (n == NWORD) /* if 3rd word, break */
break;
}
else if (isspace(*p)) /* if space */
in = 0; /* unset in flag */
}
Putting it altogether in a short example, you could do something similar to the following which takes input until the Enter key is pressed alone on an empty line, and outputting each sentence entered beginning with the third-word, or displaying the error "too few words." if a sentence with less that three words is entered, e.g.
#include <stdio.h>
#include <ctype.h>
#define MAXC 1024 /* buffer size (don't skimp) */
#define NWORD 3 /* output beginning with NWORD word */
int main (void) {
for (;;) { /* loop continually until empy-line */
char buf[MAXC] = "", /* buffer to hold line */
*p = buf; /* pointer to walk down buffer */
int n = 0, /* word counter */
in = 0; /* flag - in a word */
fputs ("\nenter sentence: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin) || *buf == '\n') { /* read line */
puts ("all done!");
break;
}
for (; *p; p++) { /* loop over each char */
if (!in && !isspace(*p)) { /* if not in word and not space */
in = 1, n++; /* set in flag, increment words */
if (n == NWORD) /* if 3rd word, break */
break;
}
else if (isspace(*p)) /* if space */
in = 0; /* unset in flag */
}
if (n == NWORD) /* if 3 or more words */
fputs (p, stdout);
else /* other wise handle error */
fputs ("too few words.\n", stderr);
}
return 0;
}
Example Use/Output
$ ./bin/thirdword
enter sentence: one two three four five
three four five
enter sentence: one two
too few words.
enter sentence: one two three
three
enter sentence:
all done!
Look things over and let me know if you have further questions.
count = 1; //shouldn't this be count = 0? and point = &arr[i + 2]; //shouldn't this be [i+1]?
The following answers both questions.
count count count count
0 1 2 3
one two three four
i+0 i+1 i+2 i+3
point = &arr[i + 2]; along with printf("%s\n", point); says that print all characters from address of arr[i + 2] till seeing \0 character

I want to read from text file that displays which character appears most or least often (keyboard or text file) ? :)

Here's my code:
include <stdio.h>
int main()
{
char str[1000], ch;
int i, frequency = 0;
printf("Enter a string: ");
gets(str);
printf("Enter a character to find the frequency: ");
scanf("%c",&ch);
for(i = 0; str[i] != '\0'; ++i)
{
if(ch == str[i])
++frequency;
}
printf("Frequency of %c = %d", ch, frequency);
return 0;
I figured that the frequency of characters code I came up with is similar. How to implement the character which appears more / less often in standard input or text file?
Also, should I use StreamReader sr = new StreamReader("example.txt") for reading normal text files for this code?
EDIT: Have to use Switch /M for most often and /L for least often.
That's a good start...
include <stdio.h>
int main()
{
char str[1000], ch,lookup_Chars[256];
int i, frequency = 0;
char counter;
printf("Enter a string: ");
gets(str);
printf("Enter a character to find the frequency: ");
scanf("%c",&ch);
for(i = 0; str[i] != '\0'; ++i)
{
lookup_Chars[str[i]]++;
}
for(counter = 0; counter<sizeof(lookup_Chars); counter++)
{
printf("Frequency of %c = %d", counter, lookup_Chars[counter]);
}
return 0;
Never, never, never use gets. It is so insecure and so susceptible to buffer overrun, it has been removed from the C standard library. Use fgets instead, just be aware that fgets will read and include the trailing '\n' in the buffer it fills (just as all legitimate line oriented input functions do, such as POSIX getline). This prevents leaving a '\n' unread in the input buffer (e.g. stdin) following each user input.
You risk Undefined Behavior because you do not validate the contents of str in any way and then you fail to validate the return of scanf to insure a character was read. (the user could cancel input in either case by generating an EOF with Ctrl+d on *nix systems of with Ctrl+z on windoze).
Further, you must understand that scanf will leave characters in the input buffer (as will fgets if the line is longer than the amount of storage you have allocated). That is one of the most common pitfalls new C programmers fall victim to. (failing to validate a complete line of input was read and failing to handle characters that remain in the input buffer)
When taking user input with fgets (which is recommended), since it reads and includes the '\n' in the buffer it fills, you simply check the length of the buffer read with strlen and then make sure the last character is a '\n'.
scanf is full of pitfalls when used for user input. While it can be used, if used correctly with it's return validated and any remaining characters emptied from stdin before your next call to scanf, you have to approach it use that way. In your case ch is the last input for your file, but try taking input for ch before reading the string and see what happens...
Putting it altogether, and adding validations for both str and ch, and adding additional comments in-line below, you could do something similar to the following:
#include <stdio.h>
#include <string.h>
#define MAXS 1024 /* if you need a constant, define one */
int main (void) {
int frequency = 0;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch;
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
if (*p++ == ch) /* compare to ch and increment to next char */
frequency++; /* increment frequency if they are equal */
printf ("\nFrequency of %c = %d\n", ch, frequency);
return 0;
}
(note: you could declare char ch[3] = ""; and use fgets to read fgets (ch, sizeof ch, stdin) and then simply compare if (*p++ == *ch) to prevent leaving the '\n' in stdin (but you would still need to validate that it was the final character read, and if not manually empty stdin))
Exammple Use/Output
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: a
Frequency of a = 10
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: p
Frequency of p = 2
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: z
Frequency of z = 0
Look things over, think about the validations that were made, and let me know if you have any further questions.
Using a Frequency Array to Capture Count of all Chars
Using a frequency array allows you to capture the frequency of all characters (or the independent count of any element of a set). Essentially, you use an array initialized to zero with one element for each member of the set you want to count the frequency of each occurrence. Since there are 128 ASCII Characters, you can simply use an array of 128 elements, e.g. int frequency[128] = {0};.
If you look at the link provided, you see the ASCII value of each character corresponds to a value between 0-127, so when looping over each character in the input string, if you increment the array index that corresponds to the character, you end up with the total count for each character in its corresponding element. For example, if p is a pointer to the beginning of str, then you can loop over each character in the string capturing their frequency in the frequency[*p] element of the array:
while (*p != '\n') {
frequency[*p]++; /* increments element corresponding to char *p */
p++; /* note: cast to (int)*p intentional omitted */
}
Now that you have the frequency for every character stored in the frequency, you can simply loop over the elements you are concerned about to determine max/min, etc... Note: the normal printable characters begin with 'space' (ASCII 32, or hex 0x20) and end with '~' (ASCII 126 or hex 0x7e`). So just limit your check of values to the printable range, e.g.
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i]; /* save least frequent count */
minc = i; /* save least frequent char */
}
if (frequency[i] > max) { /* just find max */
max = frequency[i]; /* save same for max */
maxc = i;
}
}
(note: you can further micro-divide ranges for only lowercase, uppercase, digits, etc..)
Putting that altogether, you can do something similar to the following to report the number of occurrence if the wanted char, the max occurring char, the min occurring char (and then summarize by dumping the frequency of all chars):
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAXS 1024 /* if you need a constant, define one */
#define NASCII 128 /* number of ASCII chars (includes non-printing) */
int main (void) {
int frequency[NASCII] = {0},
max = INT_MIN,
min = INT_MAX;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch,
minc = 0,
maxc = 0;
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
frequency[(int)*p++]++; /* increment element representing ch */
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i]; /* save least frequent count */
minc = i; /* save least frequent char */
}
if (frequency[i] > max) { /* just find max */
max = frequency[i]; /* save same for max */
maxc = i;
}
}
/* ouput requested char freq, and max/min chars */
printf ("\nFrequency of %c = %d\n"
"least frequent occurrence: %c = %d\n"
" most frequent occurrence: %c = %d\n\n",
ch, frequency[(int)ch], minc, min, maxc, max);
/* output frequency of all printable chars */
printf ("frequency of all printable characters:\n");
for (int i = ' '; i < '~'; i++)
if (frequency[i])
printf (" '%c' : %d\n", i, frequency[i]);
return 0;
}
Exammple Use/Output
$ ./bin/freqofc2
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: m
Frequency of m = 2
least frequent occurrence: c = 1
most frequent occurrence: a = 10
frequency of all printable characters:
' ' : 6
'a' : 10
'c' : 1
'l' : 2
'm' : 2
'n' : 4
'p' : 2
Adding /L or /M Switches for Least/Max Occurrences
To add command line switches, for a minimum number in a known order, you can simply use the allowable argument count and argument vector parameters to main(), e.g. int main (int argc, char **argv). Example:
int main (int argc, char **argv) {
...
/* validate "/L" or "/M" provided as an argument */
if (argc != 2 || (argv[1][1] != 'L' && argv[1][1] != 'M')) {
fprintf (stderr, "error: insufficient input, req'd /M or /L.\n");
return 1;
}
To test and output either the least or minimum, you simply test which character is present and act accordingly, e.g.
...
if (argv[1][1] == 'L') /* output requested lease or max */
printf ("requested least frequent occurrence: %c = %d\n\n",
minc, min);
else
printf ("requested most frequent occurrence: %c = %d\n\n",
maxc, max);
Putting that together an a complete example would be:
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAXS 1024 /* if you need a constant, define one */
#define NASCII 128 /* number of ASCII chars (includes non-printing) */
int main (int argc, char **argv) {
int frequency[NASCII] = {0},
max = INT_MIN,
min = INT_MAX;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch,
minc = 0,
maxc = 0;
/* validate "/L" or "/M" provided as an argument */
if (argc != 2 || (argv[1][1] != 'L' && argv[1][1] != 'M')) {
fprintf (stderr, "error: insufficient input, req'd /M or /L.\n");
return 1;
}
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
frequency[(int)*p++]++; /* increment element representing ch */
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i];
minc = i;
}
if (frequency[i] > max) { /* just find max */
max = frequency[i];
maxc = i;
}
}
/* ouput requested char freq, and max/min chars */
printf ("\nFrequency of %c = %d\n"
"least frequent occurrence: %c = %d\n"
" most frequent occurrence: %c = %d\n\n",
ch, frequency[(int)ch], minc, min, maxc, max);
if (argv[1][1] == 'L') /* output requested lease or max */
printf ("requested least frequent occurrence: %c = %d\n\n",
minc, min);
else
printf ("requested most frequent occurrence: %c = %d\n\n",
maxc, max);
/* output frequency of all printable chars */
printf ("frequency of all printable characters:\n");
for (int i = ' '; i < '~'; i++)
if (frequency[i])
printf (" '%c' : %d\n", i, frequency[i]);
return 0;
}
Let me know if you have any questions.

Populating one char array using fscanf changes the value of another char array

I'm first using fscanf to populate my first array then again using fscanf from the same input file to populate another array. However this seems to be shifting the values in my first array.
Here is my input:
4
abcd
efgh
ijkl
mnop
qrst
uvwx
yz12
3456
Here is my code:
#include <stdio.h>
void prints( int n, char sqr[n][n]){
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
printf("%c", sqr[i][j]);
}
printf("\n");
}
}
int main(void){
FILE *in = fopen("transform.in", "r");
FILE *out = fopen("transform.out", "w");
int num;
fscanf(in, "%d", &num);
char square[num][num];
for (int i = 0; i < num; i++){
fscanf(in, "%s", square[i]);
}
prints(num, square);
printf("\n");
char endSquare[num][num];
for (int i = 0; i < num; i++){
fscanf(in, "%s", endSquare[i]);
}
fclose(in);
prints(num, square);
printf("\n");
prints(num, endSquare);
printf("\n");
fclose(out);
return 0;
}
And here is the output I get:
abcd
efgh
ijkl
mnop
bcd
efgh
ijkl
mnop
qrst
uvwx
yz12
3456
As you can see my square array seems to be changed after I populate endSquare.
In addition to not accounting for the nul-terminating character that the %s format specifier will append, you are making things extremely hard on yourself by attempting to read lines of data with a formatted input function fscanf. When doing line-oriented input, it is far better to use a line-oriented input function such as fgets and then parse the needed information from the buffer containing the entire line of information. Why? Mixing numeric and character input with the scanf family of functions can pose many traps for those who do not account for all characters that remain in the input buffer or account for how the differing fscanf format specifiers handle leading whitespace.
Particularly, in your case when reading num, you have no way to limit the number of characters read by fscanf using the %s format specifier. You cannot include a variable field width to protect against writing beyond your array bounds. (e.g. you can't use something like %nums for %4s to insure you limit the characters read to 4) When using a VLA to hold a specific number of characters based on what is read from the first line, there just isn't an elegant way to incorporate num and validate/limit the number of characters read using fscanf.
All of this adds up to a train-wreck waiting to happen if there happens to be a stray space (or other character) at the end of one of your lines.
So, how to handle reading only 4-char into each row of square and endsquare? (note: the capital 'S' was reduced to lower-case to match normal C-style) When you need to handle lines of input, use a line oriented input function and provide a buffer sufficient to handle each line of data. I would rather use a 128-char buffer and insure I read every 4-5 char line than accidentally read a 5-char line into a 4-char buffer -- to avoid Undefined Behavior. Moreover, you can use the same buffer to read every line of data.
Next, you must validate every read and every conversion to insure you do not process garbage from the point of the failed read or failed conversion forward in your code. For example, when reading your data file, you can declare a simple buffer and read your first line as follows:
#define MAX 128
...
char buf[MAX] = "";
...
if (!fgets (buf, MAX, in)) { /* read 1st line with 'num' */
fprintf (stderr, "error: read of 'num' failed.\n");
return 1;
}
errno = 0; /* errno to check after strtol conversion */
int num = (int)strtol (buf, NULL, 10); /* convert num to int */
if (errno) { /* validate */
fprintf (stderr, "error: failed conversion of 'num'.\n");
return 1;
}
When reading each subsequent line, you need to confirm that the entire-line was read by checking for the trailing '\n' (read and included by fgets) and if it isn't present, your line is too long (handle error). You also need to know if there are num characters in the line since you are NOT storing the lines as strings (only as a character array). If there are not num-chars read, you cannot copy num-chars to square[i] or endsquare[i]. Since you are using a for loop, you also must check that each line you read is a valid line. Just because you read 4 as line one, there is no guarantee there are 8 lines in the file. (you would ideally want to use a while (fgets (buf, MAX, in)) loop to drive the rest of the input and a counter to break after 4-lines are read, but you can protect within the for loop by validating with an if (fgets (buf, MAX, in)) as well.
With that in mind, you could fill the character arrays with exactly 4-chars read from the file (or less) while checking for any unexpected empty lines with something like the following while still using your for loop:
char square[num][num];
for (int i = 0; i < num; i++) {
size_t len, n = num;
if (!fgets (buf, MAX, in)) /* read line int buf */
break; /* break if no line read */
len = strlen (buf); /* get length */
if (buf[len - 1] != '\n') { /* if no '\n' at end, too long */
fprintf (stderr, "error: line[%d] too long.\n", i);
return 1;
}
if (*buf == '\n') { /* 1st char is '\n' - empty */
fprintf (stderr, "error: empty line encountered.\n");
return 1;
}
if ((int)(len - 1) < num) /* if less than num, reduce num */
n = len - 1;
memcpy (square[i], buf, n); /* copy 'num' chars from buf */
}
You can do the same for your endsquare loop. Putting it all together, you could do something like the following (note: out is unused, so the code related to it is commented out below):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define MAX 128
void prints (int n, char (*sqr)[n])
{
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
putchar (sqr[i][j]);
}
putchar ('\n');
}
}
int main (int argc, char **argv) {
char buf[MAX] = "";
FILE *in = fopen (argc > 1 ? argv[1] : "transform.in", "r");
// FILE *out = fopen ("transform.out", "w");
if (!in /* || !out */) { /* validate both files open */
fprintf (stderr, "error: file open failed.\n");
return 1;
}
if (!(fgets (buf, MAX, in))) { /* read 1st line with 'num' */
fprintf (stderr, "error: read of 'num' failed.\n");
return 1;
}
errno = 0; /* errno to check after strtol conversion */
int num = (int)strtol (buf, NULL, 10); /* convert num to int */
if (errno) { /* validate */
fprintf (stderr, "error: failed conversion of 'num'.\n");
return 1;
}
char square[num][num];
for (int i = 0; i < num; i++) {
size_t len, n = num;
if (!fgets (buf, MAX, in)) /* read line int buf */
break; /* break if no line read */
len = strlen (buf); /* get length */
if (buf[len - 1] != '\n') { /* if no '\n' at end, too long */
fprintf (stderr, "error: line[%d] too long.\n", i);
return 1;
}
if (*buf == '\n') { /* 1st char is '\n' - empty */
fprintf (stderr, "error: empty line encountered.\n");
return 1;
}
if ((int)(len - 1) < num) /* if less than num, reduce num */
n = len - 1;
memcpy (square[i], buf, n); /* copy 'num' chars from buf */
}
prints (num, square);
putchar ('\n');
char endsquare[num][num];
for (int i = 0; i < num; i++) {
size_t len, n = num;
if (!fgets (buf, MAX, in)) /* read line int buf */
break; /* break if no line read */
len = strlen (buf); /* get length */
if (buf[len - 1] != '\n') { /* if no '\n' at end, too long */
fprintf (stderr, "error: line[%d] too long.\n", i);
return 1;
}
if (*buf == '\n') { /* 1st char is '\n' - empty */
fprintf (stderr, "error: empty line encountered.\n");
return 1;
}
if ((int)(len - 1) < num) /* if less than num, reduce num */
n = len - 1;
memcpy (endsquare[i], buf, n); /* copy 'num' chars from buf */
}
fclose(in);
prints (num, square);
putchar ('\n');
prints (num, endsquare);
putchar ('\n');
// fclose(out);
return 0;
}
Note: never use the variadic printf function to output a single-character, instead, use a function designed to output a single character like putchar (or fputc). And also note, you should preserve the count of individual lines and pass the correct number to prints in the event there are less than num lines read in any group (that is left for you).
Example Input
$ cat dat/list.txt
4
abcd
efgh
ijkl
mnop
qrst
uvwx
yz12
3456
Example Use/Output
$ ./bin/inout dat/list.txt
abcd
efgh
ijkl
mnop
abcd
efgh
ijkl
mnop
qrst
uvwx
yz12
3456
There are always more validations you can do, but at a minimum something like the above will work for your data with reasonable error checking. Look things over and let me know if you have any questions.
If you want to print squares, you could always do something like the following:
void prints (int n, char (*sqr)[n])
{
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (j)
putchar (' ');
putchar (sqr[i][j]);
}
putchar ('\n');
}
}
Example w/Modified prints
$ ./bin/inout dat/list.txt
a b c d
e f g h
i j k l
m n o p
a b c d
e f g h
i j k l
m n o p
q r s t
u v w x
y z 1 2
3 4 5 6

Resources