I'm trying to write a small menu based program to maintain records.
The user inputs a number to be used for the total amount of people to be stored (first name, last name, score). User enters info all on one line, separated by spaces, and I split them up into 3 columns (first name, last name, score), then presses enter and continues to enter more info until the max people is hit.
My problem being, when I run it, it doesn't work properly; it runs and accepts user input, but only for two students (even though I've been using numbers greater than 5 for test cases), then the program immediately ends (no error codes, just ends...) and it doesn't even make it to the menu. Can someone tell me what is wrong with my code?
int i, j, count, numberPeople, temp, choice;
char people[15][3], tempArr[20];
char *token;
printf("Please indicate number of records you want to enter (min 5, max 15): ");
scanf("%d", &temp);
while ((temp > 15) || (temp < 5)) {
printf("\nNumber not in specified range, try again.\n");
printf("Please indicate number of records you want to enter (min 5, max 15): ");
scanf("%d", &temp);
}
numberPeople = temp;
printf("\nEnter the first name, last name, and grade (put a space in between each): ");
for (i = 0; i < numberPeople; i++) {
fgets(tempArr, 20, stdin);
token = strtok(tempArr, " ");
for (j = 0; j < 3; j++) {
while (token != NULL) {
people[i][j] = *token;
printf("%s\n", token); // this is here to as a test case to see if my data was being stored.
token = strtok(NULL, " ");
}
}
}
EDITED: changed scanf to fgets
OUTPUT
Please indicate number of records you want to enter (min 5, max 15): 5
Enter the first name, last name, and grade (put a space in between each): firstname1 lastname1 85
firstname1
lastname1
85
firstname2 lastname2 84
firstname2
lastname2
Program ended with exit code: 0
One problem is you're using scanf() to read in the entire input line which has spaces between the first name, last name and grade:
for (i = 0; i < numberPeople; i++) {
scanf("%s", tempArr);
}
scanf("%s", tempArr) quits reading as soon as it hits the first space. For this loop you want to use fgets():
for (i = 0; i < numberPeople; i++) {
fgets(tempArr, 20, stdin);
}
But as #Pooya notes, this string size is too small for what you're doing. Although you allocate the two dimensional array of students and information fields, you never allocate the string space to hold their names and grades:
char people[15][3]
If you're doing this on the stack, it becomes conceptually a third dimension:
char people[15][3][24]
After this scanf(), there's still a return character left in the buffer:
scanf("%d", &temp);
it probably should be cleared out. #KevinDTimm and #bruceg hint at a problem here:
for (j = 0; j < 3; j++) {
while (token != NULL) {
people[i][j] = *token;
But I don't think Kevin's suggestion accounts for the index j. #Weather_Vane suggests adding \r\n to the strtok() separator string:
token = strtok(NULL, " ")
Otherwise your grade string (last field) will have a dangling newline. Also, you need to make a copy of the token returned by strtok(), you shouldn't store it directly.
Putting all these suggestions together and cleaning/fixing anything else I ran into, I offer the following rework:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MINIMUM_STUDENTS 5
#define MAXIMUM_STUDENTS 15
#define DATA_FIELDS 3
#define MAXIMUM_LINE_LENGTH 100
#define MAXIMUM_DATA_LENGTH 50
int main(int argc, char **argv) {
int numberPeople;
char people[MAXIMUM_STUDENTS][DATA_FIELDS][MAXIMUM_DATA_LENGTH];
printf("Please indicate number of records you want to enter (min %d, max %d): ", MINIMUM_STUDENTS, MAXIMUM_STUDENTS);
scanf("%d", &numberPeople);
while ((numberPeople < MINIMUM_STUDENTS) || (numberPeople > MAXIMUM_STUDENTS)) {
printf("\nNumber not in specified range, try again.\n");
printf("Please indicate number of records you want to enter (min %d, max %d): ", MINIMUM_STUDENTS, MAXIMUM_STUDENTS);
scanf("%d", &numberPeople);
}
printf("\n");
while ((getchar()) != '\n'); // flush the return (and anything else) after the number input above
printf("Enter the first name, last name, and grade (put a space in between each): \n");
for (int i = 0; i < numberPeople; i++) {
char tempArr[MAXIMUM_LINE_LENGTH];
fgets(tempArr, MAXIMUM_LINE_LENGTH, stdin);
char *token = strtok(tempArr, " ");
for (int j = 0; j < DATA_FIELDS && token != NULL; j++) {
strncpy(people[i][j], token, MAXIMUM_DATA_LENGTH);
// this is here to as a test case to see if my data was being stored.
printf("%s\n", people[i][j]);
token = strtok(NULL, " \r\n");
}
}
// do what you need to do with the data here!
return 0;
}
First off, use fgets() not scanf()
Next, you don't need the for (j=0; j<3; j++) part, that's handled by strtok. Instead use the following:
token = strtok(tempArr, " ");
while (token != NULL) {
people[i][j] = *token;
printf("%s\n", token); // this is here to as a test case to see if my data was being stored.
token = strtok(NULL, " ");
}
According to your sample, your input takes about 24 character but you defined tempArr[20]. because you overwrite something else in memory if you scan something more than 20 characters, it is better to have tempArr[100] or any number that makes sense for your tests.
Related
So I'm very new to programming and i have to make a code that asks the user their firs name and last name, and then to create a password with at least 3 characters and one uppercase letter and I'm very lost, my code has many issues and i don't think it makes a whole lot of sense, here it is:
#include <stdio.h>
#include <string.h>
int main()
{
char name[20], last_name[20];
printf("Please, enter your name and last name:\n");
printf("Name: ");
scanf("%s", name);
printf("Last name: ");
scanf("%s", last_name);
char userInput[64];
char pass = 0;
char password[25];
int i;
int x;
size_t length = 0;
while( pass == 0 ) {
length = 0;
pass = 0;
printf("\nPlease, create a password:\n ");
fgets(userInput, 63, stdin);
length = strlen(userInput);
if( length < 4) {
printf("The password must have at least 3 characters\n");
continue;
}
scanf(" %s", password);
for (i = 0; i < 25; i++) {
if (isupper(password[i])) {
break;
}
else if (password[i] == '\0') {
printf("\nPlease, enter at least one uppercase letter\n");
break;
}
}
}
return 0;
}
the "Please, enter your name and last name" repeats itself the first time it appears and i don't know why, and when the password doesn't have an uppercase it doesn't ask the user to create a new password, how can i fix any of this?
any help is much appreciated
I want to write a program, at first I input a number N, then I want to get the name (can consist of multiple words) and price of N items one by one. Example:
3
item a // the name can consist of multiple words
25.00
item b
12.50
item c
8.12
Next I want to process this data, however i got stuck on the scanning part. my code looks like this:
#include <stdio.h>
int main(){
int n;
char name[50];
int price;
scanf("%d\n", &n);
for(int i = 0; i < n; i++){
scanf("%s\n%d",name,&price);
printf("%s , %d", name, price);
}
printf("end");
}
This works for a single word item, but if the item has a space in it will not continue scanning. I tried using the gets() function, however I still don't have the right result. the code:
for(int i = 0; i < n; i++){
gets(name);
scanf("%d\n",&price);
printf("%s , %d\n", name, price);
}
printf("end");
returns:
3 // Input 3 items
item a // name of first item
1 // price of item 1
item b // name of item 2
item a , 1 // the print of the first item
2 // price of item 2
item c // name of item 3
item b , 2 // print of item 2
3 // price of item 3
word // no clue where this new input came from
end // end of scanning
My question is, how would I go about correctly scanning an input such as this? I also tried changing the scan function into while((c = getchar()) != '\n');, but got the same result...
Mixing gets(), scanf() is bad as scanf() tends to leave the trailing '\n' in stdin.
Using gets() is bad.
scanf("%s", ...) is not useful for reading a line of info with spaces meant to be saved.
how would I go about correctly scanning an input such as this?
A simple alternative is to read each line into a string and then parse the string.
#include <stdio.h>
int main() {
char line[100];
int n;
fgets(line, sizeof line, stdin);
sscanf(line, "%d", &n);
for(int i = 0; i < n; i++){
char name[50];
// int price;
double price;
fgets(line, sizeof line, stdin);
sscanf(line, " %49[^\n]", name);
fgets(line, sizeof line, stdin);
// sscanf(line, "%d", &price);
sscanf(line, "%lf", &price);
printf("%s , %.2f\n", name, price);
}
printf("end");
}
Advanced: Better code would check the return values of fgets(), sscanf(). Maybe replace sscanf(line, "%lf",... with strtod().
I think you could probably figure out what's happening here pretty easily if you added some more output. Let's try shall we?
Also, first of all you're really looking for a double input - not an integer. With an integer, your scan function won't match properly based on what you're looking for. But let's add some more output!
#include <stdio.h>
int main()
{
int n;
char name[50];
double price;
printf("Enter count: ");
scanf("%d", &n);
for (int i = 0; i < n; i++) {
printf("%s\n", "Please type a name followed by a newline followed by a number and then press enter:");
scanf("%s\n%lf", name, &price);
printf("%s , %lf", name, price);
}
return 0;
}
So I think what was happening in your initial attempt was a combination of things. Mostly that you were possibly pressing enter after the output from the first iteration? That will break the scanf call - since it isn't expecting to start with a \n but it is immediately expecting you to enter a name.
Second, I don't know the number you entered in the first iteration, because you didn't supply the output of it while still using scanf - so I have nothing other than speculation. You possibly used an integer on the first go, and subsequently on the remaining iterations you chose to use decimals? Again, this is only a guess.
Sometimes it is easier to just write your own functions with error checking. Below is a suggestion. You may also want to check that the numbers are non-negative.
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
void ReadLine(char result[], int resultLen)
{
int ch, i;
assert(resultLen > 0);
i = 0;
ch = getchar();
while ((ch != '\n') && (ch != EOF)) {
if (i < resultLen - 1) {
result[i] = ch;
i++;
}
ch = getchar();
}
result[i] = '\0';
}
void ReadInteger(int *i)
{
int ch, count;
count = scanf("%d", i);
if (count == 1) {
do {
ch = getchar();
} while (isspace(ch) && (ch != '\n'));
} else {
fprintf(stderr, "invalid input, integer expected\n");
exit(EXIT_FAILURE);
}
}
void ReadReal(double *x)
{
int ch, count;
count = scanf("%lf", x);
if (count == 1) {
do {
ch = getchar();
} while (isspace(ch) && (ch != '\n'));
} else {
fprintf(stderr, "invalid input, real number expected\n");
exit(EXIT_FAILURE);
}
}
int main(void)
{
char name[50];
int n;
double price;
ReadInteger(&n);
for (int i = 0; i < n; i++) {
ReadLine(name, sizeof name);
ReadReal(&price);
printf("%s, %.2f\n", name, price);
}
return 0;
}
As chux said in the comments of this answer: "All scan specifiers, except %c, %[, %n consumes leading white-spaces." So you don't need to account for them.
scanf("%s%d",name,&price);
And looking at your input, you should use float or double for the price.
scanf("%s%lf",name,&price);
Note that this works only if items are made of one word. If they can be of two or more words, you'd better use fgets
EDIT: for items made of more words you should use fgets
fgets(name, 50, stdin);
scanf("%lf",&price);
Replace your for loop with this:
for (int i = 0; i < n; i++) {
scanf(" %[^\n]\n%d", name, &price);
printf("%s , %d\n", name, price);
}
The first space skips leading spaces, and the [^\n] allows you to get more than one word as string input.
I'm trying to teach myself C, so I wrote a program to keep records for a gradebook! In my effort to learn pointers, I'm trying to convert one of my beginning projects of arrays to pointers. I want to convert my 2d array to pointers. Below is my original program to with the 2d array, and below that is my attempt to convert it to 2d pointer array.
ORIGINAL PROGRAM
int numberPeople, choice, i, j;
char people[15][3][100];
printf("Please indicate number of records you want to enter (min %d, max %d): ", 5, 15);
scanf("%d", &numberPeople);
while ((numberPeople < 5) || (numberPeople > 15)) {
printf("\nNumber not in specified range, try again.\n");
printf("Please indicate number of records you want to enter (min %d, max %d): ", 5, 15);
scanf("%d", &numberPeople);
}
printf("\n");
while ((getchar()) != '\n'); // flush the return (and anything else) after the number input above
printf("Enter the first name, last name, and grade (put a space in between each): \n");
for (i = 0; i < numberPeople; i++) {
char tempArr[MAXIMUM_LINE_LENGTH];
fgets(tempArr, MAXIMUM_LINE_LENGTH, stdin);
char *token = strtok(tempArr, " ");
for (j = 0; j < DATA_FIELDS && token != NULL; j++) {
strncpy(people[i][j], token, MAXIMUM_DATA_LENGTH);
token = strtok(NULL, " \r\n");
}
}
ATTEMPT AT 2D ARRAY -> POINTER
int numberPeople, choice, i, j;
char* people;
printf("Please indicate number of records you want to enter (min %d, max %d): ", 5, 15);
scanf("%d", &numberPeople);
people = (char*)(malloc(numberPeople*DATA_FIELDS*sizeof(char)));
while ((numberPeople < 5) || (numberPeople > 15)) {
printf("\nNumber not in specified range, try again.\n");
printf("Please indicate number of records you want to enter (min %d, max %d): ", 5, 15);
scanf("%d", &numberPeople);
}
printf("\n");
while ((getchar()) != '\n'); // flush the return (and anything else) after the number input above
printf("Enter the first name, last name, and grade (put a space in between each): \n");
for (i = 0; i < numberPeople; i++) {
char* tempArr;
fgets(tempArr, 100, stdin); // Thread 1: EXC_BAD_ACCESS code=1 address=0x0
char *token = strtok(tempArr, " ");
for (j = 0; j < 3 && token != NULL; j++) {
strncpy(people, token, 50);
token = strtok(NULL, " \r\n");
}
}
During the person input step is when it breaks. It works for the first entry, but then it hits a breakpoint (I'm using Xcode), and it reads "EXC_BAD_ACCESS", and I'm not too sure what this means, any tips would help, thanks!
Yes, you can convert to all pointers but I wouldn't recommend it. Here's an example:
char ***people = malloc(sizeof(*people) * 15);
for (int i = 0; i < 15; i++) {
people[i] = malloc(sizeof(*people[i]) * 3);
for (int j = 0; j < 3; j++) {
people[i][j] = malloc(sizeof(*people[i][j]) * 100);
}
}
This would re-create the array char people[15][3][100];. As you can see, it's messy. Plus, you have to free all that mem when you are done.
I cleaner way would be to use a struct:
struct Student {
char firstName[100];
char lastName[100];
char grade[100];
};
struct Student people[15];
This will set aside as much memory as the array. You could also dynamically allocate as many students as needed:
struct Student *people = malloc(sizeof(*people) * 20);
That will get 20 students. You can access them using array notation:
printf("%s %s %s", people[0].firstName, people[0].lastName, people[0].grade);
And you only need to call free once.
this pair of lines is not correct:
char* tempArr;
fgets(tempArr, 100, stdin);
the declaration of the pointer: tempArr is not allocating any actual memory to contain the line read by the the call to fgets() Suggest:
#define MAX_INPUT_LEN (100)
...
char tempArr[MAX_INPUT_LEN];
fgets( tempArr, MAX_INPUT_LEN, stdin );
Also, the returned value from the call to fgets() should be checked (!=NULL) to assure the operation was successful.
I need to write a simple program (no fancy pointer stuff, no library functions. It's for educational purposes) that reads the first and second names from a user and prints them out in a single line separated by a space. I am not getting the result and I am not sure why:
# include <stdio.h>
//the program loosely simulates the behaviour of strcpy
main(){
char fname[16], sname[16], cat[31];
int i, j;
printf("Please enter your first name: ");
scanf("%s", fname);
printf("Please enter your second name: ");
scanf("%s", sname);
for (i=0; fname[i] != '\0'; cat[i++] = fname[i++])
;
cat[i+1] = ' '; //adds a space between the tokens
for (j=i+1; sname[j] != '\0'; cat[j++] = sname[j++])
;
printf("The final result is:\n%s", cat);
return 0;
}
You have several problems. First, since cat has to be big enough to hold the first two strings and a space between them, it should be declared cat[32] -- 15 characters of first name, 15 characters of surname, 1 space, and 1 trailing null byte.
You're putting the space between the words in the wrong place. The first loop left i holding the next position in cat, so it should be:
cat[i] = ' ';
Next, your array indexes in the second loop are incorrect. The positions in cat are correct, because they start from where you left off the previous loop. But you need to start from 0 in sname. So this loop should be:
for (j = i+1, k = 0; sname[k] != 0; cat[j++] = sname[k++])
;
Finally, after concatenating the two strings, you need to append a null byte to the result, to indicate the end.
cat[j] = 0;
Another problem is that you're incrementing i twice each time through the first loop, since you use cat[i++] = fname[i++]. Each of those i++ will increment the variable. You need to separate the assignment from the increments:
for (i=0; fname[i] != '\0'; i++) {
cat[i] = fname[i];
}
Here's a final version of the script that works:
# include <stdio.h>
//the program loosely simulates the behaviour of strcpy
int main() {
char fname[16], sname[16], cat[32];
int i, j, k;
printf("Please enter your first name: ");
scanf("%s", fname);
printf("Please enter your second name: ");
scanf("%s", sname);
for (i=0; fname[i] != '\0'; i++) {
cat[i] = fname[i];
}
cat[i] = ' ';
for (j = i+1, k = 0; sname[k] != 0; cat[j++] = sname[k++]) {
}
cat[j] = 0;
printf("The final result is: %s\n", cat);
return 0;
}
I realize you set yourself a challenge to try to learn how to do something specific (and I see you are making progress towards your goal). But I always like to keep in mind there are lots of ways to get the job done -- especially in C. You know you could just do this with printf, right?
char fname[16], sname[16];
int i, j;
printf("Please enter your first name: ");
scanf("%s", fname);
printf("Please enter your second name: ");
scanf("%s", sname);
printf("%s %s\n", fname, sname);
Watch the index you use :
cat[i++] = fname[i++] and cat[j++] = sname[j++]
try to increment 'i' and 'j' at the end of your loop :
for (i=0; fname[i] != '\0'; ++i)
cat[i] = fname[i];
// ...
for (j=0; sname[j] != '\0';++j)
cat[j+i+1] = sname[j];
I'm trying to make a program that will read up to 20 words entered by the user and stored in an array of strings. The program will ask for additional words until 20 words have been entered or until the word 'done' has been entered. The idea is that these words will then be entered into a matrix to create a word search program. I'm stuck on scanning in the words entered by the user. I'm a new programmer so any words of advice is very beneficial.
#include <stdio.h>
int main(void)
{
char string[20][100];
printf("Enter up to 20 words to hide in the puzzle.\n");
printf("Enter the word 'done' after your last word if entering less than 20 words.\n");
scanf("%s\n",c)
printf("Entered words:\n");
return 0;
}
#include<stdio.h>
#include<string.h>
int main(void)
{
char words[20][100];
char temp[100]="\0";
int i=0;
int end=0; //0 false and 1 true
printf("Enter 20 words or enter done to exit.\n");
while(i <=19 && end==0)
{
strset(temp,'\0');// resets array temp to NULL's everytime
scanf(" %99[^\n]",temp); //this is scan set, to read a string without '\n'
printf("Given:%s\n\n",temp);
if(strcmpi(temp,"done")==0)//compares given input with "done".if "done" is entered. zero is returned
end=1;//when 0 is returned this end=1 will break the loop.
else//if input is not given "done" then copy temp array to words[i].
{
strcpy(words[i],temp);
i++;
}
}
}
Instead of using words[20][100] directly, I have used a temporary array named temp to initially store the input,because at the end i don't want to store "done" into words[20][100].Assuming that "done" is used only to end the input process and it is not the actual word to store.But you can change this program to your need.
This is a straight-forward but bug-fixed variant of the code provided by developer3466402 in his answer.
I've used a for loop instead of a while loop since that neatly summarizes the action in the while loop. I added n to record how many words were entered, leaving i as a loop control variable (yes, once upon a long time ago I wrote Fortran too).
#include <stdio.h>
#include <string.h>
int main(void)
{
char words[20][100];
int i = 0;
int n;
printf("Enter up to 20 words to hide in the puzzle.\n");
printf("Enter the word 'done' after your last word if entering less than 20 words.\n");
for (i = 0; i < 20; i++)
{
printf("Enter word %2d:\n", i+1);
if (scanf("%99s", words[i]) != 1 || strcmp(words[i], "done") == 0)
break;
}
n = i;
printf("%d words entered\n", n);
for (i = 0; i < n; i++)
printf("Word %2d = [%s]\n", i+1, words[i]);
return 0;
}
It worked OK for me entering 0, 1, many and 20 words. We can debate the newline after the prompt; it has pros and cons, but that's generally true. I chose to echo the data after the loop rather than within the loop. Be aware that with some programs, echoing in a loop can appear to work where echoing after the loop shows a problem.
Example run (with an early exit):
$ ./rw
Enter up to 20 words to hide in the puzzle.
Enter the word 'done' after your last word if entering less than 20 words.
Enter word 1:
aleph
Enter word 2:
null
Enter word 3:
absolute
Enter word 4:
twaddle and nonsense
Enter word 5:
Enter word 6:
Enter word 7:
elephants are done for
Enter word 8:
Enter word 9:
8 words entered
Word 1 = [aleph]
Word 2 = [null]
Word 3 = [absolute]
Word 4 = [twaddle]
Word 5 = [and]
Word 6 = [nonsense]
Word 7 = [elephants]
Word 8 = [are]
$
You can dynamically allocate memory to store words. If the user inputs more than 20 words, then you can use realloc function to allocate more memory.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int wlimit = 20;
int wcount = 0;
int retval; // to save the return value of scanf
char wstring[100+1]; // +1 for the terminating null byte
char **wlist = malloc(wlimit * sizeof *wlist);
if(wlist == NULL) {
// handle it
printf("not enough memory to allocate\n");
return 1;
}
char *temp;
while(1) {
if(wcount >= wlimit) {
wlimit *= 2;
temp = wlist;
wlist = realloc(wlist, wlimit);
if(temp == NULL) {
printf("not enough memory to allocate\n");
wlist = temp;
}
}
retval = scanf("%100s", wstring);
// if the input string is done, then break out of the
// loop else keep taking input from the user
if(retval != 1 || strcmp(wstring, "done") == 0)
break;
// strdup function creates a new string which is a duplicate
// of the input string and returns a pointer to it which can
// be freed using free
wlist[wcount++] = strdup(wstring);
}
// do stuff with wlist
// after done, free the memory
for(int i = 0; i < wcount; i++)
free(wlist[i]);
free(wlist);
wlist = NULL;
// stuff
return 0;
}
I think you wish something like below:
#include<stdio.h>
int main(void)
{
char words[20][100];
int i = 0;
printf("Enter up to 20 words to hide in the puzzle.\n");
printf("Enter the word 'done' after your last word if entering less than 20 words.\n");
while (i < 20) {
printf("Entered words:\n");
if (scanf("%99s", words[i]) != 1 || strcmp(words[i], "done") == 0)
break;
printf("%s\n", words[i]);
i++;
}
return 0;
}