I'm working on something small and I have run into a really small but disturbing problem - the program works in gcc, but crashes in visual studio, when it gets to the fgets command:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void registerUser(char *username);
char path[256] = "C:\\Users\\magshimim\\Desktop\\cproject\\bank\\";
int main() {
int choice = 0;
char username[10];
printf("Welcome to nikitosik's bank program!\n"
"Choose an option :\n"
"0 - Register\n"
"1 - Login to existing account\n");
scanf("%d", &choice);
getchar(); // catch enter
if (!choice) {
registerUser(username);
} else {
//login();
}
}
void registerUser(char *username) { // username is passed, for verification later
FILE *userinfo; // file that contains info of all registered users
FILE *userpathf; // file to open for txt file
char usertxt[256]; // path of data
char userpath[256]; // path of new user data file
char password[15];
strcat(userpath, path);
strcat(usertxt, path);
strcat(usertxt, "users.txt");
userinfo = fopen(usertxt, "a");
printf("Choose a username(max 10 letters): ");
fgets(username, 10, stdin);
username[strcspn(username, "\n")] = 0;
strcat(userpath, username);
strcat(userpath, ".txt");
userpathf = fopen(userpath, "w");
fclose(userpathf);
printf("Choose a password(max 15 letters): ");
fgets(password, 15, stdin);
fprintf(userinfo, "%s\n%s", username, password);
fclose(userinfo);
}
thanks in advance
As above really or initialize the strings first.
memset(userpath, 0, sizeof(userpath));
memset(usertxt, 0, sizeof(usertxt));
Your program has multiple issues:
you concatenate a string into an uninitialized array: this has undefined behavior and may well explain the observed behavior on different systems as undefined behavior may take multiple forms from no consequence at all to a crash or possibly even worse misfortune.
you do not test the return values of scanf, fopen, fgets(). Any invalid or unexpected input may trigger more undefined behavior.
you do not check for potential buffer overflow in strcat(). You should instead use snprintf().
Here is a safer version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int registerUser(char *username, size_t size);
char path[] = "C:\\Users\\magshimim\\Desktop\\cproject\\bank\\";
int main(void) {
int choice = 0;
int c;
char username[10];
printf("Welcome to nikitosik's bank program!\n"
"Choose an option :\n"
"0 - Register\n"
"1 - Login to existing account\n");
if (scanf("%d", &choice) != 1)
return 1;
// consume the rest if the input line
while ((c = getchar()) != EOF && c != '\n')
continue;
if (choice == 0) {
registerUser(username, sizeof(username));
} else {
//login();
}
return 0;
}
int registerUser(char *username, size_t size) { // username is passed, for verification later
char usertxt[256]; // path of data
char userpath[256]; // path of new user data file
char password[17];
FILE *userinfo; // file that contains info of all registered users
FILE *userpathf; // file to open for txt file
printf("Choose a username(max %d letters): ", (int)(size - 2));
if (!fgets(username, size, stdin))
return -1;
username[strcspn(username, "\n")] = 0; // strip newline if any
printf("Choose a password(max 15 letters): ");
if (!fgets(password, sizeof(password), stdin))
return -1;
password[strcspn(password, "\n")] = 0; // strip newline if any
if (snprintf(usertxt, sizeof(usertxt), "%s%s", path, "users.txt") >= (int)sizeof(usertxt))
return -1;
if (snprintf(userpath, sizeof(userpath), "%s%s.txt", path, username) >= (int)sizeof(userpath))
return -1;
userinfo = fopen(usertxt, "a");
if (userinfo == NULL)
return -1;
// create user file
userpathf = fopen(userpath, "w");
if (userpathf != NULL)
fclose(userpathf);
// add user info
fprintf(userinfo, "%s\n%s\n", username, password);
fclose(userinfo);
return 0;
}
Related
This program should ask you to add member (people) to a struct and print them on a file but after the first for loop just stop working and jump over the name part. I just found that thing that allow you to add space to a string, tried it but no success...
I tried to remove it and it work without any problem so the [^\n] make something go wrong.
What is wrong ?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Staff {
char Surname[100];
char Name[100];
int age;
char spec[100];
int id;
} person;
void write();
void leggi();
void trova();
int main() {
write();
}
void write() {
int i = 0;
int n = 1;
int r;
FILE *fp;
fopen_s(&fp, "index.txt", "w+");
if (fp == NULL) {
printf("Failed to open file\n");
exit(1);
}
fprintf(fp, "%d\n", i);
for (i = 0; i < n; i++) {
printf("Surame:\n");
scanf_s("%[^\n]s", person.Surname, 100);
fprintf(fp, "%s\t\t", person.Surname);
//loop just get over the name part
printf("Name:\n"); //after the first loop
scanf_s("%s", person.Name, 100);
fprintf(fp, "%s\t", person.Name);
printf("Age:\n");
scanf_s("%d", &person.age);
fprintf(fp, "%d\t", person.age);
printf("Specialization\n");
scanf_s("%s", person.spec, 100);
fprintf(fp, "%s\n", person.spec);
printf("Want to enter another? 1=yes 0=no...\n");
scanf_s("%d", &r);
if (r == 1)
n = n + 1;
}
rewind(fp);
fprintf(fp, "%d\n", i);
fclose(fp);
}
There are multiple problems in your code:
you use the so called secure functions fopen_s, scanf_s etc, but you do not check the return values to detect invalid input. You should instead use standard functions, pass the appropriate arguments and check the return values.
using scanf_s is actually non portable: the scanf_s function defined in Annex K of the C Standard requires the length argument after the pointer to have size_t type, whereas the function with the same name in the Microsoft library uses type UINT, which has a different representation on 64-bit versions of their Windows OS. A classical case of the Embrace, enhance and extinguish strategy. In Standard C, one should write: scanf_s("%s", person.Name, (size_t)100) or better:
scanf_s("%s", person.Name, sizeof person.Name)
there is no need to open the output file for update with "w+", just use "w".
you rewind the stream pointer back to the beginning of file and overwrite the number of entries at the start of the file. This works as long as you have less than 10 entries, but beyond that, the number has more digits so some characters in the file will be corrupted. You could use a format with padding such as "%6d\n" which would allow for up to 1 million records without risks.
"%[^\n]s" is not a correct scanf format: you should just write "%[^\n]" or better " %99[^\n]" to skip initial white space and limit the input to 99 characters.
Here is a modified version:
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Staff {
char Surname[100];
char Name[100];
int age;
char spec[100];
int id;
};
void write(void);
void leggi(void);
void trova(void);
int main() {
write();
}
int flush_input(void) {
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
return c;
}
void write(void) {
int n = 0;
int r;
FILE *fp = fopen("index.txt", "w");
if (fp == NULL) {
fprintf("Failed to open file index.txt: %s\n",
strerror(errno));
exit(1);
}
fprintf(fp, "%6d\n", n);
for (;;) {
struct Staff person = { 0 };
printf("Surname:\n");
if (scanf(" %99[^\n]", person.Surname) != 1)
break;
flush_input();
fprintf(fp, "%s\t\t", person.Surname);
//loop just get over the name part
printf("Name:\n"); //after the first loop
scanf(" %99[^\n]", person.Name);
flush_input();
fprintf(fp, "%s\t", person.Name);
printf("Age:\n");
scanf("%d", &person.age);
flush_input();
fprintf(fp, "%d\t", person.age);
printf("Specialization\n");
scanf(" %99[^\n]", person.spec, 100);
flush_input();
fprintf(fp, "%s\n", person.spec);
n++;
printf("Want to enter another? 1=yes 0=no...\n");
if (scanf("%d", &r) != 1 || r != 1) {
flush_input();
break;
}
flush_input();
}
rewind(fp);
// update the entry count on 6 characters
fprintf(fp, "%6d\n", n);
fclose(fp);
}
Change the call of scanf below for entering strings by inserting a space in the beginning of the format string. For example instead of this call
scanf_s("%[^\n]s", person.Surname, 100);
(where the letter s must be removed from the format string) write
scanf_s(" %[^\n]", person.Surname, ( rsize_t )100);
^^^^^^^^
This allows to skip leading white space characters in the input buffer.
Pay attention to that changing the condition or the for loop the was as you are doing
for (i = 0; i < n; i++) {
//...
if (r == 1)
n = n + 1;
}
makes the code unclear. Instead of the for loop you could use do-while loop.
This question already has answers here:
gets() does not work
(3 answers)
Why is gets() not consuming a full line of input?
(7 answers)
Closed 1 year ago.
I am trying to read students data (name, department, roll, sgpa, cgpa), I used fgets function to read name and department, but it skips to the department and can't read name.
here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[100];
char department[50];
int roll;
float sgpa[12];
float cgpa;
} Student;
void add(FILE *fp);
void modify(FILE *fp);
void display(FILE *fp);
FILE * del(FILE * fp);
int main(int argc, char **argv) {
int option;
FILE *fp;
fp = fopen("db.txt", "rb+");
if (fp == NULL) {
fp = fopen("db.txt", "wb+");
if (fp == NULL) {
printf("Can't open the database");
return 0;
}
}
printf("\t1. Add Student");
printf("\n\t2. Modify Student");
printf("\n\t3. Show All Student");
printf("\n\t4. Remove Student");
printf("\n\tEnter Your Option :--> ");
scanf("%d",&option);
switch(option)
{
case 1:
add(fp);
break;
case 2:
// modify(fp);
break;
case 3:
// display(fp);
break;
case 4:
// fp = del(fp);
break;
default:
printf("Unkonw option\n");
}
return 0;
}
void add(FILE *fp) {
Student std;
int i;
float cgpa;
fseek(fp,0,SEEK_END);
printf("\n\tEnter Full Name of Student: ");
fflush(stdin);
fgets(std.name,100,stdin);
std.name[strlen(std.name)-1]='\0';
printf("\n\tEnter Depertment Name: ");
fflush(stdin);
fgets(std.department,50,stdin);
std.department[strlen(std.department)-1]='\0';
printf("\n\tEnter Roll number: ");
scanf("%d",&std.roll);
printf("\n\tEnter SGPA for 12 semesters\n");
for(i=0,cgpa=0; i<12; i++)
{
scanf("%f",&std.sgpa[i]);
cgpa+=std.sgpa[i];
}
cgpa/=12.0;
std.cgpa=cgpa;
fwrite(&std,sizeof(std),1,fp);
}
Is there a way of limiting scanf in C?
I did this, if you're having that many problems with string input giving an eye on my other answer might help you
Here's a more specific answer to your problem on message you place what you wanna print before the input and StringInput you place the array you're using to hold the message
I hope this works since you also use a array for it
also i see this:
fp = fopen("db.txt", "rb+");
if (fp == NULL) {
fp = fopen("db.txt", "wb+");
if (fp == NULL) {
printf("Can't open the database");
return 0;
}
}
which may not be what you want because if it fails because of a sudden glitch by using wb+ you will overwrite it
“r+” – Searches file. Opens the file for both reading and writing. If opened successfully, fopen() loads it into memory and sets up a pointer which points to the first character in it. Returns NULL, if unable to open the file.
“w+” – Searches file. If the file exists, its contents are overwritten. If the file doesn’t exist, a new file is created. Returns NULL, if unable to open the file.
FUNCAUX_MAX_STRING is a macro and you define it like so:
#define FUNCAUX_MAX_STRING 100
This makes it so that the number or elements is 100 and you can easily change every number thats using the macro by simply changing the value once
void readString(char message[FUNCAUX_MAX_STRING], char StringInput[FUNCAUX_MAX_STRING], int maxChars)
{
int sizeString;
do // Repete leitura caso sejam obtidas strings vazias
{
printf("%s", message);
fgets(StringInput, maxChars, stdin);
sizeString = strlen(StringInput);
if (sizeString == 1)
{
printf("Nao foram introduzidos caracteres!!! . apenas carregou no ENTER \n\n");
}
}
while (sizeString == 1);
if(StringInput[sizeString-1] != '\n')
{
cleanBufferStdin();
}
else
{
StringInput[sizeString-1] = '\0';
}
}
void cleanBufferStdin(void)
{
char chr;
do
{
chr = getchar();
}
while (chr != '\n' && chr != EOF);
}
I have been asked to create a program where I have to encrypt multiple pieces of information using a Caesar Cipher. I understand the concept behind it but what I'm having trouble visually is how to enter pieces of data within the function. For example, I have encrypted passwords saved in a file ("hrkk1" meaning "pass1" and so on). I have to create a cipher function to read the input from a scanf and strcmp so it matches what's in the file allowing the user to login.
Whats the best way to validate the user input and make "pass1" turn into "hrkk1" so it matches what's in the file and allows user login?
Thank you
This is the code I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <windows.h>
void checkValid(void);
void loginDetails(char username[5][6], char password[5][9]);
void encryption(char username[5][6], char password[5][9]);
int main(void)
{
FILE *EP;
FILE *UN;
char username[5][6];
char password [5][9], ch, key;
EP = fopen("encrypted_passwords.txt", "r");
fscanf(EP, "%s %s %s %s %s", password[0], password[1],
password[2], password[3], password[4]);
fclose(EP);
UN = fopen("username.txt", "r");
fscanf(UN, "%s %s %s %s %s", username[0], username[1], username[2],
username[3], username[4]);
fclose(UN);
printf("Welcome.");
loginDetails(username, password);
return 0;
}
void loginDetails(char username[5][6], char password[5][9])
{
int i;
char nurseUsername[6];
char nursePassword[6];
bool useValid = 0;
bool passValid = 0;
printf("Please Enter your username: \n");
scanf("%s", nurseUsername);
for (i = 0; i < 5; i++)
{
if(strcmp(nurseUsername, username[i]) == 0)
{
useValid = 1;
}
}
if(useValid != 1)
{
printf("\nError. Invalid Username. Returning to menu.\n");
Sleep(1000);
system("cls");
main();
}
else
{
printf("\nPlease enter your password: \n");
scanf("%s", nursePassword);
}
for(i = 0; i < 5; i++)
{
if((strcmp(nurseUsername, username[i]) == 0) &&
(strcmp(nursePassword, password[i]) == 0))
{
passValid = 1;
}
if(passValid != 1)
{
printf ("Error. Invalid Password. Returning to menu.\n");
Sleep(1000);
system("cls");
main();
}
else
{
printf("\nLogin Successful. Loading menu.\n");
Sleep(1000);
system("cls");
patientEntry();
}
}
}
You need to use the shifting of the character in c. This is possibile with a simple addition( or subtraction) on a char value.
Pay attention your example doesn't shift the number character and maybe also the character doesn't go out the alphabet, and it take in consideration also the capital letters. So pay attention when you do the addition to not exceed the range of capital or non capital letter. My suggestion is to use ascii table.
I seem to be having an issue with some code. The purpose of the code is to take a phrase and convert it to pig latin.
It seems that this code will not take user input, at the block where we say if (x == 1). What it will do is it will automatically take NULL as input to fgets, and I'm clueless as to why.
I've spent entirely too much time on this problem, and I would appreciate any advice on how to improve this code. Please leave a comment on how I can improve my questions in the future.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int pigLatin()
{
char phrase[250] = { 0 };
char pigPhrase[300] = { 0 };
char * tokens[300] = { 0 };
char fileName[260] = { 0 };
FILE * read = NULL;
FILE * write = NULL;
int i = 0;
int x;
int size;
while (i < 10000) {
i++;
x = 0;
printf("Enter one(1) to input via console, two(2) to input via .txt, or (3) to exit:\n");
scanf_s("%d", &x);
if (x == 1) {
printf_s("Enter your Phrase Do not include:\nany punctuation, words less than 2 letters long, or words seperated by blanks:");
fgets(phrase, sizeof phrase, stdin);
phrase[strlen(phrase) - 1] = '\0';
printf_s("\nPhrase Entered:%s\n", phrase);
system("pause");
}
else if (x == 2)
{
printf("Enter name of input file:\n");
scanf_s("%s", fileName, 260);
printf("File name:\n%s\n", fileName);
if (fopen_s(&write, fileName, "r") == 0)
{
scanf_s("%s", phrase, 260);
}
}
else if (x == 3)
{
break;
}
else
{
printf("Invalid Statement\n");
break;
}
}
return 0;
}
scanf("%d", &number); will read an integer but leave everything else in the stream including the '\n' generated by pressing [Enter] after entering the number. This newline left in the stream is then consumed by fgets() without giving you a chance for input.
Clear the stream after using scanf():
int clear(FILE *stream)
{
int ch; // reads until EOF or a newline is encountered:
while((ch = fgetc(stream)) != EOF && ch != '\n');
}
// ...
int number;
if(scanf("%d", &number) != 1) {
// handle error;
}
clear(stdin);
char foo[42];
fgets(foo, sizeof(foo), stdin);
// ...
I want to enter multiple printfs but i dont get opportunity to enter.
I can enter only 1, but after that it just ends the programme.I tried with do while but it didnt work
int main()
{
int number;
char username[30]="";
char fullName[30]="";
char password[30]="";
printf("Do you want to log in(1) or register (2)? \n");
scanf("%d",&number);
if (number==2)
{
printf("username : ");
scanf("%s",&username);
printf("Full name : ");
scanf("%s",&fullName);
printf("Password : ");
scanf("%s",&password);
printf("Repeat password : ");
scanf("%s",&password);
}
return 0;
}
Read full lines using fgets() into a suitably large buffer, then parse that.
Note that %s will stop at the first blank character, so a full name of "Mr X" will leave "X" in the input buffer, grabbing that for the password and so on. It's really not a robust way of getting input.
I can enter only 1, but after that it just ends the programme.
Of course, as the code has if (number==2) #Scadge
If you enter "2", consider the following:
scanf("%s",&fullname); will not save spaces or other white-spaces into fullname. Entering a full name like "John Doe" will save "John" into fullname and "Doe" into password.
Avoid using scanf().
Rather than use scanf() to read user input, read user input with fgets(). This is a fine opportunity for helper functions that can handle various input issues.
int read_int(const char *prompt) {
if (prompt) fputs(prompt, stdout);
fflush(stdout); // insure output is written before asking for input
char buffer[40];
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
return NULL;
}
int i;
if (sscanf(buffer, "%d", &i) == 1) {
return i;
}
// TBD - what should code do if invalid data entered. Try again?
}
char *read_line(char *dest, sizeof size, const char *prompt) {
if (prompt) fputs(prompt, stdout);
fflush(stdout); // insure output is written before asking for input
char buffer[size * 2 + 1]; // form buffer at _least 1 larger for \n
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
return NULL;
}
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') buffer[--len] = '\0';
if (len >= size) {
// input too big - how do you want to handle this?
TBD_Code();
}
return strcpy(dest, buffer);
}
Now use these 2 helper functions for clean user input
// printf("Do you want to log in(1) or register (2)? \n");
// scanf("%d",&number);
number = read_int("Do you want to log in(1) or register (2)? \n");
...
// printf("username : ");
// scanf("%s",&username);
read_line(username, sizeof username, "username : ");
// printf("Full name : ");
// scanf("%s",&fullName);
read_line(fullName, sizeof fullName, "fullName : ");
Additional code could be added to check for end-of-file, extremely long lines, int range testing, etc.
Use c library function fgets().
#include <stdio.h>
int main(){
Char username[10];
printf(“Username: “);
fgets(username,10,stdin);
}