I want to create a program that opens and writes to 3 different text files, the names a user inputs.
The condition would be that if last names end with certain characters, they would be saved to a specific text file.
For example, if the user inputs a last name that ends with ian it should be saved to the ARMENIA.TXT folder.
Here is my code and the issues I encounter with it:
struct names {
char firstName[20];
char lastName[30];
} person;
int main() {
FILE *arm, *ita, *esp;
char ian[] = "ian";
char ini[] = "ini";
char ez[] = "ez";
char response;
char *ret;
arm = fopen("C:\\Programming\\ARMENIA.TXT", "wt");
ita = fopen("C:\\Programming\\ITALIA.TXT", "wt");
esp = fopen("C:\\Programming\\ESPANIA.TXT", "wt");
if (arm, ita, esp == NULL) {
printf("Error: The archives could not be created.");
return 1;
} else {
printf("Operation Successful: Archives created.\n\n");
}
do {
fflush(stdin);
printf("\n\nPlease input your first name: ");
scanf("%s", &person.firstName);
printf("Please input your last name: ");
scanf("%s", &person.lastName);
if (ret = strstr(person.lastName, ian)) {
fwrite(person.lastName, 1, strlen(person.lastName), arm);
fwrite(person.firstName, 1, strlen(person.firstName), arm);
}
if (ret = strstr(person.lastName, ini)) {
fwrite(person.lastName, 1, strlen(person.lastName), ini);
fwrite(person.firstName, 1, strlen(person.firstName), ini);
}
if (ret = strstr(person.lastName, ez)) {
fwrite(person.lastName, 1, strlen(person.lastName), ez);
fwrite(person.firstName, 1, strlen(person.firstName), ez);
}
printf("\n\nWould you like to enter another person into the archive?: (y) or (n): ");
scanf(" %c", &response);
} while (response == 'y');
printf("\n\nThe operation has finished.\n\n");
fclose(arm);
fclose(ita);
fclose(esp);
return 0;
}
Issue: Will save to folder if last name contains ian (or ini / ez) in ANY part of the last name. How do I make the condition only if it ENDS with these strings?
Issue: Will crash if last name contains ini or ez -- basically, only first If statement works.
Issue: Needs to be saved as Lastname, Firstname -- For now, it saves as LastnameFirstname.
Your program has multiple issues.
Some of the have been addressed by Ray and Stephen Lechner, but here are some more:
The reason for the crashes is you pass string to fwrite instead of FILE* stream pointers: fwrite(person.lastName, 1, strlen(person.lastName), ini); should be written:
fwrite(person.lastName, 1, strlen(person.lastName), ita);
This is an indication that you compile without proper warnings enabled. Let the compiler help avoid such silly mistakes with -Wall for gcc or /W3 for msvc.
Note also that you should use fprintf to properly format the output to your text files. For example:
if (strEndsWith(person.lastName, "ian")) {
fprintf(arm, "%s, %s\n", person.lastName, person.firstName);
}
Here is an improved version:
#include <stdio.h>
#include <string.h>
int strEndsWith(const char *str, const char *ending) {
size_t len1 = strlen(str);
size_t len2 = strlen(ending);
return len1 >= len2 && !strcmp(str + len1 - len2, ending);
}
int main(void) {
FILE *arm = fopen("C:\\Programming\\ARMENIA.TXT", "wt");
FILE *ita = fopen("C:\\Programming\\ITALIA.TXT", "wt");
FILE *esp = fopen("C:\\Programming\\ESPANIA.TXT", "wt");
if (arm == NULL || ita == NULL || esp == NULL) {
printf("Error: The archives could not be created.\n");
return 1;
} else {
printf("Operation Successful: Archives created.\n\n");
}
for (;;) {
char firstName[20];
char lastName[30];
char response;
printf("\n\nPlease input the first name: ");
if (scanf("%19s", firstName) != 1)
break;
printf("Please input the last name: ");
if (scanf("%29s", lastName) != 1)
break;
if (strEndsWith(lastName, "ian")) {
fprintf(arm, "%s, %s\n", lastName, firstName);
}
if (strEndsWith(lastName, "ini")) {
fprintf(ita, "%s, %s\n", lastName, firstName);
}
if (strEndsWith(lastName, "ez")) {
fprintf(esp, "%s, %s\n", lastName, firstName);
}
printf("\n\nWould you like to enter another person into the archive?: (y) or (n): ");
if (scanf(" %c", &response) != 1 || response != 'y')
break;
}
printf("\n\nThe operation has finished.\n\n");
fclose(arm);
fclose(ita);
fclose(esp);
return 0;
}
In addition to the issues Ray pointed out, see the following simple code for a strEndsWith-function. Hope it helps.
int strEndsWith(const char* str, const char* ending) {
size_t strlenStr = strlen(str);
size_t strlenEnding = strlen(ending);
if (strlenStr < strlenEnding)
return 0;
const char* strAtEnding = str + strlenStr - strlenEnding;
return (strcmp(strAtEnding, ending) == 0);
}
There are a number of unrelated issues here.
arm = fopen("C:\\Programming\\ARMENIA.TXT", "wt");
That should be "w+", not "wt". Same for the others. Or just "w", unless you want to both read and write.
if (arm, ita, esp == NULL)
The comma operator evaluates its left operand and discards it, then evaluates its right operand and keeps that value. So this is equivalent to if (esp == NULL)
fflush(stdin);
This is meaningless. When printing, the system is allowed to buffer output and then print it all at once, which can speed things up. fflush forces it to actually print anything that's currently in the buffer. It's useful if, for example, you want to prompt the user for input and need to make sure the prompt shows up.
As such, it only works on an output stream. (This error is actually taught in some terrible references, so I suspect this one isn't your fault. You should stop using whatever reference told you to do this.)
Addendum: As Olaf points out in the comments, this invokes undefined behavior, which is a topic unto itself. The short and imprecise version is that any undefined behavior creates bugs that are really hard to track down.
if (ret = strstr(person.lastName, ian))
This tells you that ian is in person.lastName somewhere, but not that it's at the end. For example, suppose the last name is "ian in ez". It'll match all three of your conditions. As an aside, you don't need to store ian in a variable; you can just do strstr(person.lastName, "ian").
Related
I'm creating a file management program for a project in C and I have this error which is probably obvious to most programmers but since I'm really bad I can't spot what I have done wrong. My main program is an interface which asks users for a file name and assigns it to the array fileName.
int main() {
char fileName[50];
assignFileName(fileName);
char option[2];
int checkInput;
do {
printf("File management program. Options :\n '1' for File operations (Create, copy, delete or display file)\n '2' for Line operations (Append, delete, display or insert line to file)\n '3' for General operations (Display change log or number of lines for file)\n '4' to select a new file\n '9' to exit program\n");
printf("aaa %s\n", fileName); //first printf check - prints "aaa" and value in fileName
checkInput = checkUserInput(option);
printf("aaa %s\n", fileName); // second printf check = prints only "aaa"
if (checkInput == 1) {
//... etc
}
void assignFileName(char *fileName) {
printf("Enter file name to operate on, or 'E' to exit the program.\n");
do {
if ((fgets(fileName, 50, stdin)) != NULL) {
if (fileName[strlen(fileName)-1] = '\n') {
fileName[strlen(fileName)-1] = '\0';
}
if (strlen(fileName) == 1 && *fileName == 'E') {
exit(0);
} else if (strlen(fileName) == 0) {
printf("Error : Please enter a file name or 'E' to exit.\n");
}
} else {
perror("Error assigning file name ");
}
} while (strlen(fileName) == 0);
}
I'm fairly sure this code is fine. There's probably lots of ways to make it more efficient and if anyone wants to offer their input I will take it into account. However, the problem is later on in the code. I have 2 printf statements to check the value of fileName. After the first one, everything seems to be alright but then the for the second one the value of fileName seems to be cleared, so something is clearly happening in checkUserInput. All checkUserInput does is check the user enters a single digit number :
void flush() {
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
}
}
int checkUserInput(char *input) {
if (fgets(input, 3, stdin) != NULL) {
printf("you entered %c\n", input[0]);
if (input[1] == '\n') {
return 1;
} else {
flush();
printf("Error : Please enter one of the options given.\n");
}
} else {
printf("Error : Please try again.\n");
}
return 0;
}
I put more printf statements to error check and it seems after the call to fgets(input, 3, stdin) the value in fileName is cleared. Can anyone explain to me why this is the case? I'm not even passing the array fileName to checkUserInput so I don't even know how the program is changing it. This is a link to what the console displayed : (can't post images sorry not 10 rep).
https://cdn.discordapp.com/attachments/708320229737889832/802557193043050516/unknown.png
All help would be very appreciated. Thanks.
if (fileName[strlen(fileName)-1] = '\n') should be:
if (fileName[strlen(fileName)-1] == '\n')
Note that you can strip the trailing newline with this simple line:
filename[strcspn(filename, "\n")] = '\0';
error was char option[2] when it should be char option[3]. Thanks to #Nate Eldredge and #M Oehm
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE *userfile, *pwfile, *usernamesPasswords, *readUsernamesPasswords;
char u, p, user[20][25], pass[20][20], userLine[25], passLine[20];
int i = 0;
int j = 0;
int userIndex = 0;
int passIndex = 0;
userfile = fopen("usernames.txt", "r");
pwfile = fopen("passwords.txt", "r");
if (userfile == NULL || pwfile == NULL) {
printf("Cannot open file \n");
exit(0);
}
u = fgetc(userfile);
while (u != EOF) {
if ( u != '\n'){
userLine[userIndex++] = u;
} else {
userLine[userIndex] = '\0';
strcpy(user[i], userLine);
userIndex = 0;
i++;
}
u = fgetc(userfile);
}
fclose(userfile);
p = fgetc(pwfile);
while (p != EOF) {
if ( p != '\n'){
passLine[passIndex++] = p;
} else {
passLine[passIndex] = '\0';
strcpy(pass[j], passLine);
passIndex = 0;
j++;
}
p = fgetc(pwfile);
}
fclose(pwfile);
usernamesPasswords = fopen("usernamesPasswords.txt", "w");
int w, k;
char newLine[1024];
for (w=0;w<20;w++) {
strcat(newLine, user[w]);
int q = strlen(newLine);
for (k=0;k<(25-q);k++) {
strcat(newLine, " ");
}
strcat(newLine, pass[w]);
strcat(newLine, "\n");
strcat(newLine, "\0");
fputs(newLine ,usernamesPasswords);
strcpy(newLine, "");
}
fclose(usernamesPasswords);
printf("\nDo you want to display the new file? Enter (y) to view and any other key to exit\n");
char word;
scanf("%c",&word);
if (word == 'y') {
readUsernamesPasswords = fopen("usernamesPasswords.txt", "r");
if (readUsernamesPasswords == NULL) {
printf("Cannot open file \n");
exit(0);
}
char r;
while((r=fgetc(readUsernamesPasswords))!=EOF) {
printf("%c", r);
}
fclose(readUsernamesPasswords);
}
return 0;
}
Expected Output:
Moodie 123456
Intelllligent password
Happee 12345678
Mischeivous qwerty
SweetKristy 123456789
KristyHoney 12345
BubblySnowflake 1234
AngelicPrincessKristy 111111
FairyPrincessKristy 1234567
BabyKristyButterfly dragon
daffyusers 123123
magpiedoodle baseball
aspiringthreat abc123
landmarksspreader football
cavaliervanquish monkey
chatteringparma letmein
veggiehydrogen 696969
teethunsure shadow
rumpboast master
lidarstrudel 666666
Instead...
123456
password
12345678
qwerty
123456789
12345
1234ke
111111incessKristy
1234567sKristy
dragonutterfly
123123
baseball
abc123
footballer
monkeysh
letmein
696969
shadow
master
666666
This only happens when I try to output on terminal.
The actual usernamesPasswords.txt file comes out as expected.
The left side of strings only seem to get printed only when I newline in place of whitespace...
I even tried fgets and fread but similar output. I have read other posts about eating or consuming so I tried using fgets or fread as suggested and even tried unget. did not seem to work. I have been looking all over stack overflow to no avail.
If this is a duplicate, I apologize in advanced. I have really tried to look all over the web for couple hours before deciding to post.
I usually don't post cuz stack overflow usually has everything already...
Please help! Thank you!
You cannot call strcat or strlen on uninitialized storage. It's Undefined Behaviour. String functions generally require input string arguments to be correctly NUL-terminated. (Without the terminator, there is no way to tell where the string ends.)
And strcat(s, "\0") is a no-op because the second argument is effectively an empty (zero-length) string. (And, as above, if s is not already NUL-terminated, it is Undefined Behaviour.)
In your program, this part of code is having few problems:
char newLine[1024];
for (w=0;w<20;w++) {
strcat(newLine, user[w]);
int q = strlen(newLine);
for (k=0;k<(25-q);k++) {
strcat(newLine, " ");
}
strcat(newLine, pass[w]);
strcat(newLine, "\n");
strcat(newLine, "\0");
fputs(newLine ,usernamesPasswords);
strcpy(newLine, "");
}
Problems like newLine buffer is not initialized and used in strcat. Concatenating empty string has no effect - strcat(newLine, "\0");. You can solve these problems by simply initializing buffer with {0} and instead of concatenating an empty string, assign \0 at appropriate buffer index. But you don't need to do all this strcat's, instead you can simply do all this in just one line like this:
char newLine[1024];
for (w=0;w<20;w++) {
snprintf (newLine, 1024, "%-25s%s\n", user[w], pass[w]);
fputs(newLine ,usernamesPasswords);
}
snprintf appends a terminating null character after the content written to the buffer.
Few points:
- You should check the return value of fopen.
- The return type of fgetc is int and not char and you are taking its return value in a char type.
- You need to take care of few things in your program like your program works on an assumption that usernames.txt and passwords.txt contains exact 20 lines. It's better to make it flexible.
I have been working with text files but somehow I have been getting a strange error. When I try to get the 1st, 2nd, 4th, and 5th parts of a string (the ':' is the delimiter), I get a weird response. This is what I am trying to read:
1000:Product:0.75:5:0
And I get this sort of answer:
8 |X |0
75(
Here is the code:
int main(){
char c,buff[100],prod[30],id[8],stock[8],vendas[8];
int i=0,n=0,num=0;
FILE *fp;
fp=fopen("products.txt","r+");
printf("Lista de produtos(Num |Produto |Stock |Vendas)\n");
while(fgets(buff,100,fp)){
for(n=0;n<strlen(buff);n++){
if(buff[n]==':'){
num++;
i=0;
}
else if((buff[n]!=':')&&(num==0)){
id[i]=buff[n];
i++;
}
else if((buff[n]!=':')&&(num==1)){
prod[i]=buff[n];
i++;
}
else if((buff[n]!=':')&&(num==3)){
stock[i]=buff[n];
i++;
}
else if((buff[n]!=':')&&(num=4)){
vendas[i]=buff[n];
i++;
}
}
i=0;
num=0;
printf("%s |%s |%s |%s\n",id,prod,stock,vendas);
memset(id,0,8);
memset(prod,0,30);
memset(stock,0,8);
memset(vendas,0,8);
}
printf("Prima qualquer tecla para sair");
getchar();
return 0;
}
Any help would be appreciated. Sorry if this question or my code isn't the best.
Have a great day!
In case the format string does not start with :, you never initialize i.
In case it does start with : then num==0 will never get executed.
You never null terminate the strings.
if(buff[n]==':') ... else if((buff[n]!=':' is redundant.
num=4 is a bug, enable compiler warnings or get a better compiler.
You never check if the file was opened correctly nor if you reached the end of the file.
The fitst time through invokes undefined behaviour because you have not initialised the content of each field buffer.
You also do not check for buffer overruns when filling the fields in. If a field is as long or longer than the array you are putting it in, there will be a buffer overrun and no terminating \0
Also in each else if you do not need the (buff[n]!=':') condition.
Also, the last else if has an assignment in the conditional n = 4 (although this actually does not affect the result).
At least two errors:
Initialize your arrays first before trying to use them...
while(fgets(buff,100,fp)){
memset(id,0,8);
memset(prod,0,30);
memset(stock,0,8);
memset(vendas,0,8);
for(n=0;n<strlen(buff);n++){
Assignment in test, replace:
if((buff[n]!=':')&&(num=4)){
with
if((buff[n]!=':')&&(num==4)){
^
You may be making the logic much more difficult that it needs to be. When you must break a line of text up into words based on a delimiter, you should be thinking strtok or strsep. strtok is the normal routine used to tokenize a line of text on delimiters, breaking the line up into individual words. (strsep is primarily used when there is a chance of encountering an empty-field, as in a large .csv file).
Your tokens are simple the ':' char and the '\n' at the end. You could simply declare char *delim = ":\n"; and be covered.
While your are free to daisy-chain if, then, else if, .... together, a switch on a counter may make life easier and the code more readable.
Putting those pieces together, you could do something like the following:
#include <stdio.h>
#include <string.h>
enum { ISV = 8, PROD = 30, BUFF = 100 }; /* constants */
int main (int argc, char **argv) {
char buff[BUFF] = "", prod[PROD] = "", id[ISV] = "",
stock[ISV] = "", vendas[ISV] = "", *delim = ":\n";
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed.\n");
return 1;
}
while (fgets (buff, sizeof buff, fp)) { /* for each line of input */
int i = 0, n = 0; /* tokenize buff into words */
for (char *p = strtok (buff, delim); p; p = strtok (NULL, delim)) {
switch (i) {
case 0: strncpy (id, p, ISV); /* handle id */
if (strlen (p) >= ISV)
id[ISV-1] = 0;
i++;
break;
case 1: strncpy (prod, p, PROD); /* handle prod */
if (strlen (p) >= PROD)
prod[PROD-1] = 0;
i++;
break;
case 2: strncpy (stock, p, ISV); /* handle stock */
if (strlen (p) >= ISV)
stock[ISV-1] = 0;
i++;
break;
case 3: strncpy (vendas, p, ISV); /* handle vendas */
if (strlen (p) >= ISV)
vendas[ISV-1] = 0;
i++;
break;
default: break;
}
}
n++;
printf ("id: %s, prod: %s, stock: %s, vendas: %s\n",
id, prod, stock, vendas);
if (fp != stdin) fclose (fp); /* close file */
}
return 0;
}
Example Input File
$ cat dat/prod.txt
1000:Product1:0.75:5:0
1001:Product2:0.90:2:0
1002:Product3:0.55:8:0
Example Use/Output
$ ./bin/prod dat/prod.txt
id: 1000, prod: Product1, stock: 0.75, vendas: 5
id: 1001, prod: Product2, stock: 0.90, vendas: 2
id: 1002, prod: Product3, stock: 0.55, vendas: 8
Look things over and let me know if you have further questions.
I have spent the last 4 or 5 hours on something that is probably quite simple, I have tried many different methods. What I am trying to do is make my program save the end of each line in a saved test file. This would be the players names followed by the shirt number or weight etc. The file is saved in a format like this:
First name is:xxxxxx
Last name is:xxxxx
Date of birth is:xxxxxx
I want my code to be able to just store the xxxxxx in a separate variable so then i can un-encrypt it as only them parts are encrypted.
The code I have got so far is
int main()
{
int dob;
char lastname[15], *ptr, filename[25], line[40], storage[200], a[15];
FILE *file;
printf("Please enter the last name of your player: ");
scanf("%s", lastname);
printf("\nPlease enter the date of birth of your player: ");
scanf("%d", &dob);
printf("\n\n");
sprintf(filename, "%s%6d.pat", lastname, dob);
printf("%s", filename);
file = fopen(filename, "r");
while(fgets(line, sizeof(line), file) != NULL)
{
fprintf(stdout, "%s", line);
}
fscanf(file, "%s: %s\n", storage, a);
printf("%s %s", storage, a);
I am now currently trying to use this piece of code to the get the last string after the : then apply a small decryption to it, but i seem to get an infinite loop of just the name after the first line.
do
{
if(sscanf(line,"%*[^:]:%19s",s)==1)
{
for(i = 0; i < slen; i++) /*encrypting firstname*/
{
slen = strlen(s);
s[i] = (char)((s[i] - 'a' - 4) % 26 + 'a');
if(s == '\0')
{
break;
}
}
printf("%s",s);
slen = strlen(s);
slen--;
}
}while(slen > 0);
You can make use of sscanf function -
char s[20];
if(sscanf(line,"%*[^:]:%19s",s)==1){ //check return of sscanf
/* %*[^:] will read string till ':' and discard it */
printf("%s",s);
}
This will store your desired string in s.
Note - Data in your file should in form as you show in question.
If you want data even outside loop then use a 2-d char array instead . Increment index of array in loop as you read new data from file.
EDIT-
Dont use uninitialized variable slen before calculating string length . You can re-write your code as -
int i=0;
if(sscanf(line,"%*[^:]:%19s",s)==1)
{
slen = strlen(s);
while(i<slen)/*encrypting firstname*/
{
//slen = strlen(s);
s[i] = (char)((s[i] - 'a' - 4) % 26 + 'a');
if(s == '\0')
{
break;
}
i++;
}
printf("%s",s);
slen--;
}
So I have a program that takes user input and compares it to a specific line in a file, however the final line will always be credited as incorrect, so can someone solve this for me?, thanks.
File content (just a list of random words)
Baby
Milk
Car
Face
Library
Disc
Lollipop
Suck
Food
Pig
(libraries are stdio,conio and string)
char text[100], blank[100];
int c = 0, d = 0;
void space(void);
int main()
{
int loop = 0;
char str[512];
char string[512];
int line = 1;
int dis = 1;
int score = 0;
char text[64];
FILE *fd;
fd = fopen("Student Usernames.txt", "r"); // Should be test
if (fd == NULL)
{
printf("Failed to open file\n");
exit(1);
}
do
{
printf("Enter the string: ");
gets(text);
while (text[c] != '\0')
{
if (!(text[c] == ' ' && text[c] == ' '))
{
string[d] = text[c];
d++;
}
c++;
}
string[d] = '\0';
printf("Text after removing blanks\n%s\n", string);
getch();
for(loop = 0;loop<line;++loop)
{
fgets(str, sizeof(str), fd);
}
printf("\nLine %d: %s\n", dis, str);
dis=dis+1;
str[strlen(str)-1] = '\0';
if(strcmp(string,str) == 0 )
{
printf("Match\n");
score=score+2;
}
else
{
printf("Nope\n");
score=score+1;
}
getch();
c=0;
d=0;
}
while(!feof(fd));
printf("Score: %d",score);
getch();
}
For any input on the last line, the output will always be incorrect, I believe this is something to do with the for loop not turning it into the next variable, but seeing as the <= notation makes this program worse, I really just need a simple fix for the program thanks.
Some observations:
You must never use gets (it is not even in the C11 standard anymore). Instead of gets(text) use fgets(text, sizeof(text), stdin) – this way a long input will not overflow the text array.
There will be stuff printed at the end because you don't check the return value of either the gets or the fgets, so when end of file occurs for either the file or for user input the rest of that iteration still runs. fgets returns NULL if it didn't read anything – check for that instead of using feof.
You remove newlines from the file input but not from the user input, so the comparison will always fail when you switch from gets to fgets (which doesn't strip linefeeds). The second (otherwise pointless) comparison of text[c] against ' ' should be against '\n'.
edit: Also, in case the last line of your file does not end in a linefeed, the comparison will fail on the last line because you don't check if the last character is a linefeed before you remove it.
The for (loop = 0; loop < line; ++loop) -loop is pointless because line is always 1, so the body is only executed once.
You have unnecessarily global variables which the program hard to follow. And, for instance, your local text[64] overshadows the global text[100], so if you think you are modifying the global buffer, you are not. If your code is complete, none of the variables should be global.
The function getch() is non-standard. There is no easy direct replacement, so you may just accept that you are not writing portable code, but it's something to be aware of.