how to extract numbers from char array and add them up - arrays

i want to get input from the user to get the name, then check the input against a char array filled by an external text file, then store the number associated with the name to a variable and be able to add up the number with any more that is associated with the same name
code:
int cnt_space(int i, int count, char input[], char strIndex)
{
strIndex = input[0];
while (strIndex != '\0'){
strIndex = input[i];
if (isspace(strIndex))
count++;
i++;
}
return (count);
}
/*bool divisable5()
{
}*/
char * dispChange(char name[])
{
enum { MAXL = 40, MAXC = 50 };
char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */
int i, n = 0, index;
FILE *fp = fopen("coins.txt", "r");
if (!fp) { /* valdiate file open for reading */
printf ("failed to open file");
}
if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */
fprintf (stderr, "error: virtual memory exhausted 'lines'.\n");
}
while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
char *p = lines[n]; /* assign pointer */
for (; *p && *p != '\n'; p++) {} /* find 1st '\n' */
*p = 0, n++; /* nul-termiante */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
int *change = 0, finalChange = 0;
char changeStr[2];
changeStr[0] = 0;
/* print lines */
for (i = 0; i < n; i++){
if (strstr(lines[i], name) != NULL){
for (index = 0; index < strlen(lines[i]); index++){
if(isdigit(lines[i][index])){
printf("test %s %d", lines[i], lines[i][index]);
if(changeStr[0] == 0){
changeStr[1] = "1";
printf("%s", changeStr);
strcpy(changeStr, lines[i][index]);
}
else{
strcat(changeStr, lines[i][index]);
}
}
}
sscanf(changeStr, "%d", change);
finalChange += change;
main();
}
else if (i == n-1 && strstr(lines[i], name) == NULL){
printf("Name not found, please try again\n\n");
dispChange(name);
}
}
printf ("Customer:\n%s%dcents\n\n", name, finalChange);
free (lines); /* free allocated memory */
}
char * getName()
{
char ch;
int i = 0, count = 0;
char name[100];
printf("Enter a name(spaces not accepted): ");
fgets(name, sizeof name, stdin);
count = cnt_space(i, count, name, ch);
if (count > 1){
printf("Error: Spaces not accepted!\n\n");
free(name);
getName();
}
else{
dispChange(name);
}
}
int main()
{
int choice;
char option[100];
printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
fgets(option, sizeof option, stdin);
if (option[1] != '\n'){
printf("Invalid entry!\n\n");
main();
}
else{
choice = (int)option[0];
if (isdigit(choice)){
if (choice == 49){
getName();
}
else if (choice == 50){
printf("Program has ended\n");
exit(0);
}
else{
printf("Invalid option!\n\n");
main();
}
}
else{
printf("Invalid entry!\n\n");
main();
}
}
return 0;
}
somehow doing lines[i][index] does not get the number? it should get number 3 but its getting number 51
text file contains:
Justin 60
Jane 30
Jared 90
MinZhan 95
Andreas 80
GuoCong 95
David 45
Ngiap 35
Teng 10
Teow 95

To answer your question, it IS getting 3 as an ASCII character, which has a decimal value of 51. You are printing it with %d, which is the decimal formatter.
There are, however, a lot of things going on in this program.
The first is cnt_space:
int cnt_space(int i, int count, char input[], char strIndex)
{
strIndex = input[0];
while (strIndex != '\0'){
strIndex = input[i];
if (isspace(strIndex))
count++;
i++;
}
return (count);
}
You are always passing 0 for i, the start of the string, so you do not need to pass i at all. You are also always passing 0 for count, and thus do not need to pass count at all. You are also passing ch, which is never read but only written to, so should be declared only in cnt_space. But you do not even need an actual count, you just need to know if the name contains a space character at all, which you can determine with a function like strpbrk. I would remove your cnt_space function entirely and replace it with:
// these values came from the man page for isspace
if (strpbrk(name, " \f\n\r\t\v")) {
printf("Error: Spaces not accepted!\n\n");
continue;
}
You'll notice I put continue in the code, which doesn't quite fit with what you've got because you recursively call main. That's typically frowned upon, and I changed it to continue in anticipation of replacing this with some kind of loop.
Going to skip over dispChange for a minute and look at your getName function:
char * getName()
{
char ch;
int i = 0, count = 0;
char name[100];
printf("Enter a name(spaces not accepted): ");
fgets(name, sizeof name, stdin);
count = cnt_space(i, count, name, ch);
if (count > 1){
printf("Error: Spaces not accepted!\n\n");
free(name);
getName();
}
else{
dispChange(name);
}
}
Good practice would be to make this function static, as it is only called within this file. You also specify that it returns a character pointer, but it does not actually return anything. If you want to keep this as the caller to dispChange, I would recommend changing the return type to void since there is no return, and changing the function name to reflect this. The recursive call should be replaced with a loop in this case to prevent a literal stackoverflow. Also, you free name but free is to return memory to the heap (from malloc or some other memory allocation). You are calling it on the stack, which I would imagine crashes. I would rewrite it to look more like this:
static void getNameAndDisplayChange()
{
char name[100];
do {
printf("Enter a name(spaces not accepted): ");
fgets(name, sizeof(name), stdin);
// these values came from the man page for isspace
if (strpbrk(name, " \f\n\r\t\v")) {
printf("Error: Spaces not accepted!\n\n");
continue;
}
dispChange(name);
} while (0);
}
Now let's take a look at main, before we get into the weeds of dispChange. You had:
int main()
{
int choice;
char option[100];
printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
fgets(option, sizeof option, stdin);
if (option[1] != '\n'){
printf("Invalid entry!\n\n");
main();
}
else{
choice = (int)option[0];
if (isdigit(choice)){
if (choice == 49){
getName();
}
else if (choice == 50){
printf("Program has ended\n");
exit(0);
}
else{
printf("Invalid option!\n\n");
main();
}
}
else{
printf("Invalid entry!\n\n");
main();
}
}
return 0;
}
The C standard allows void arguments to main, though you may make your program more flexible by taking argc and *argv[] to accept a name on input. There are better ways to validate the input here, but we can clean it up a lot just by changing the recursion. I would also recommend using defines such as EXIT_SUCCESS and EXIT_FAILURE instead of return 0. Instead of converting the input to an integer and using the ASCII values to determine the action, you can use strcmp to keep it more readable. I would also recommend creating a "readline" function that strips the newline from user input to make it even more readable, but you can do that later. The refactored main would look something like:
int main()
{
char option[100] = "\0";
while (strcmp(option, "2") != 0) {
printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
fgets(option, sizeof(option), stdin);
if (strcmp(option, "1\n") != 0 && strcmp(option, "2\n") != 0) {
printf("Invalid entry: %s!\n\n", option);
continue;
}
if (strcmp(option, "2\n") == 0) {
printf("Program has ended\n");
break;
}
getNameAndDisplayChange();
}
return EXIT_SUCCESS;
}
For dispChange, typically with a function this big I would recommend breaking it into smaller functions, but I think we can shrink it quite a bit:
char * dispChange(char name[])
{
enum { MAXL = 40, MAXC = 50 };
char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */
int i, n = 0, index;
FILE *fp = fopen("coins.txt", "r");
if (!fp) { /* valdiate file open for reading */
printf ("failed to open file");
}
if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */
fprintf (stderr, "error: virtual memory exhausted 'lines'.\n");
}
while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
char *p = lines[n]; /* assign pointer */
for (; *p && *p != '\n'; p++) {} /* find 1st '\n' */
*p = 0, n++; /* nul-termiante */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
int *change = 0, finalChange = 0;
char changeStr[2];
changeStr[0] = 0;
/* print lines */
for (i = 0; i < n; i++){
if (strstr(lines[i], name) != NULL){
for (index = 0; index < strlen(lines[i]); index++){
if(isdigit(lines[i][index])){
printf("test %s %d", lines[i], lines[i][index]);
if(changeStr[0] == 0){
changeStr[1] = "1";
printf("%s", changeStr);
strcpy(changeStr, lines[i][index]);
}
else{
strcat(changeStr, lines[i][index]);
}
}
}
sscanf(changeStr, "%d", change);
finalChange += change;
main();
}
else if (i == n-1 && strstr(lines[i], name) == NULL){
printf("Name not found, please try again\n\n");
dispChange(name);
}
}
printf ("Customer:\n%s%dcents\n\n", name, finalChange);
free (lines); /* free allocated memory */
}
Again, I would make it static as it is confined to this file. It also does not return anything, so I would change the return type to void. Since we aren't changing name, I would let the caller know that by defining it as const as well.
There are some flaws in this function, you read from fp even if it is determined the file could not be opened. You compare fp to stdin, which I don't see a way for that to happen right now (maybe a feature enhancement?). If you have to read from stdin, it makes sense that you would want to load it all into memory, but you'll definitely want to do that once and outside of this function. strstr is used in such a way that it may match unintended names, such as "Charley" will match "O'Charley". To fix that, you will want to make sure that the name matches the start of the line, and that it ends in a space. strlen is computed inside of a loop, which is just wasted CPU cycles, since the length won't change.
You can reduce a lot of complexity and increase scalability by operating on the file one line at a time, instead of loading it all into memory. Again, if you have to support stdin, you will not want to make this change, but rather have a separate function that loads a FILE * into an array, something like loadData(FILE *fp, lines). This refactor is based on how your sample works, iterating through the file for every name.
static void dispChange(const char name[])
{
enum { MAXC = 50 };
char line[MAXC];
int nameLen = strlen(name);
FILE *fp = fopen("coins.txt", "r");
if (!fp) { /* valdiate file open for reading */
printf ("failed to open file\n");
return;
}
int finalChange = 0;
while (fgets(line, MAXC, fp)) { /* read each line */
if (strstr(line, name) == line && line[nameLen] == ' ') {
int change;
sscanf(&line[nameLen + 1], "%d", &change);
finalChange += change;
}
}
fclose(fp);
printf ("Customer:\n%s %d cents\n\n", name, finalChange);
}
I hope this is helpful, and not just me doing the assignment for you.

Related

How do I read integers and characters from a text file and store them in separate arrays?

I am trying to read inputs from a file that contain strings such as "Jane 30", "Chris 40", and so on, line by line. I then need to store each name with it's corresponding number in the same index of different arrays, so "Jane" in one array with index 0, and 30 in an integer array with index 0.
This is the code I have so far, but I am struggling to figure out how to extract the integers into a separate array, and the characters into another. Please help.
#include stdio.h
#include stdlib.h
#include DarrensInfo.
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char names[150];
int change[50];
int x, y;
while(!feof(coinfile)){
fgets(names, 150, coinfile);
y = 0;
for(x=0; names[x]; x++){
if(names[x] <= '0' && names[x] <= '9'){
change[y] = names[x];
y++;
}
}
}
fclose(coinfile);
return 0;
}
#define COINS_MAX_LINES 150
#define MAX_LINE_LENGTH 100
#define MAX_NAME_LENGTH 50
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char line[MAX_LINE_LENGTH];
char names[COINS_MAX_LINES][MAX_NAME_LENGTH];
int change[COINS_MAX_LINES];
int lineno = 0;
int i = 0;
while(fgets(line, MAX_LINE_LENGTH, coinfile))
{
sscanf(line, "%s %d", names[lineno], &change[lineno]);
++lineno;
}
fclose(coinfile);
for (i = 0; i<lineno;++i)
printf("Name = %s Change = %d\n", names[i], change[i]);
return 0;
}
After the end of the while loop, names array & change arrays will contain what you want. I have printed it out in the second loop
You need a array of character arrays. Define the variable names as names[150][30], assuming the length of each name will not exceed more than 30 characters.
Use a combination of fgets and fscanf to parse the line of entry in the file into separate variables as you desired
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE * coinfile;
coinfile = fopen("coins.txt", "r");
char names[150][30];
int change[150];
int x = 0, y, i;
char buf[100];
while(!feof(coinfile)){
if(fgets(buf, 100, coinfile) != NULL) {
sscanf(buf, "%s %d", names[x], &change[x]);
x++;
}
}
fclose(coinfile);
puts("-------");
for (int i = 0; i < x; i++)
printf("%s %d\n", names[i], change[i]);
return 0;
}
I am trying to read inputs from a file that contain strings such as "Jane 30", "Chris 40", and so on, line by line
You're reading a file that might contain strings such as "Jane 30", "Chris 40", and so on; but might also contain millions of typing mistakes and/or other errors; and therefore you need to detect errors and clearly inform the user what the error is so that they can easily understand the problem, then find the mistake, then fix it.
For this reason none of the C library functions are ever useful.
Instead build a parser as a finite state machine. For example (untested):
// State
int state = 0;
int column = 0;
int line = 1;
char current_name[MAX_NAME_LENGTH];
int name_length;
int number;
// Main loop
for(;;) {
int c = fgetc(file);
column++;
switch(state) {
case 0: /* At start of new line */
if(c == FEOF) {
return OK;
} else if(isdigit(c)) {
printf("ERROR: Number found at start of line (missing name), on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isalpha(c)) {
name_length = 0;
current_name[name_length++] = c;
state = 1;
} else if(c == '\n') {
line++
} else if(isspace(c)) {
} else {
printf("ERROR: Bad character at start of line, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 1: /* In middle of name */
if(c == FEOF) {
printf("ERROR: File ends in the middle of a name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
printf("ERROR: No whitespace between name and number, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isalpha(c)) {
if(name_length >= MAX_NAME_LENGTH) {
printf("ERROR: Name too long (max length is %d), on line %d at column %d\n", MAX_NAME_LENGTH, line, column);
return NOT_OK;
}
current_name[name_length++] = c;
} else if(c == '\n') {
printf("ERROR: No number after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isspace(c)) {
state = 2;
} else {
printf("ERROR: Bad character in middle of name, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 2: /* Between name and number */
if(c == FEOF) {
printf("ERROR: File ends after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
number = c - '0';
state = 3;
} else if(c == '\n') {
printf("ERROR: No number after name, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isspace(c)) {
} else {
printf("ERROR: Bad character after name, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 4: /* In middle of number */
if(c == FEOF) {
printf("ERROR: File ends in middle of number, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(isdigit(c)) {
if(number > INT_MAX / 10) {
printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
return NOT_OK;
}
number *= 10;
if(number > INT_MAX - (c - '0') ) {
printf("ERROR: Number is too large, on line %d at column %d\n", line, column);
return NOT_OK;
}
number += c - '0';
} else if(c == '\n') {
create_new_entry(current_name, name_length, number);
line++
state = 0;
} else if(isspace(c)) {
state = 5;
} else {
printf("ERROR: Bad character after number, on line %d at column %d\n", line, column);
return NOT_OK;
}
break;
case 5: /* Trailing white space before end of line */
if(c == FEOF) {
printf("ERROR: File ends between number and end of line, on line %d at column %d\n", line, column);
return NOT_OK;
} else if(c == '\n') {
line++
create_new_entry(current_name, name_length, number);
state = 0;
} else if(isspace(c)) {
} else {
printf("ERROR: Unknown characters between number and end of line, on line %d at column %d\n", line, column);
return NOT_OK;
}
}
}
If you're trying to parse names that might look like "King Charles 3", it will be difficult to use scanf. It's not actually that difficult to do this sort of thing without using static sized buffers, and that's a good practice to get used to. You'll want to avoid fgets, since that is difficult to use without a fixed size. Note that growing arrays is notoriously difficult, so I make no claims as to the correctness of the following. One has to do this sort of exercise every few months to be reminded why we don't do this sort of thing in C:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE * Fopen(const char *, const char *);
void * Realloc(void *, size_t);
/* Remove trailing whitespace */
void trim_white(char *b, char *end)
{
while( end > b && isspace(*--end)) {
*end = '\0';
}
}
int
main(int argc, char **argv)
{
char *path = argc > 1 ? argv[1] : "stdin";
FILE *coinfile = argc > 1 ? Fopen(path, "r") : stdin;
size_t asize = 16;
size_t bsize = 0;
char *buf = NULL;
char **names = Realloc(NULL, asize * sizeof *names);
long *change = Realloc(NULL, asize * sizeof *change);
unsigned line_number = 0;
ssize_t char_read;
while( buf = NULL, (char_read = getline(&buf, &bsize, coinfile)) != -1) {
char *space;
char *end;
trim_white(buf, buf + char_read);
space = strrchr(buf, ' ');
if(space == NULL) {
fprintf(stderr, "Invalid input on line %d (no space)\n", line_number + 1);
exit(EXIT_FAILURE);
}
change[line_number] = strtol(space + 1, &end, 10);
if(*end != '\0') {
fprintf(stderr, "Invalid input on line %d at '%s'\n", line_number + 1, space + 1);
exit(EXIT_FAILURE);
}
*space = '\0';
names[line_number] = buf;
if(++line_number == asize) {
asize *= 2;
names = Realloc(names, asize * sizeof *names);
change = Realloc(change, asize * sizeof *change);
}
}
return EXIT_SUCCESS;
}
FILE *
Fopen(const char *path, const char *mode) {
FILE *fp = fopen(path, mode);
if( fp == NULL ) { perror(path); exit(EXIT_FAILURE); }
return fp;
}
void *
Realloc(void *buf, size_t s)
{
buf = realloc( buf, s );
if( buf == NULL) { perror("realloc"); exit(EXIT_FAILURE); }
return buf;
}

Having trouble reading from text file into a struct array

I recently started at university with C programming (beginner course), and now we are doing our final examination which is about a patients' database.
I'm required to read data from a text file to a struct array (size 10000). The file contains 2 string arrays (personal identification string (10 numbers seperated by a '-') and name string), 1 int array containing photo references and 1 integer containing the amount of photo references per patient. I have tried fscanf but the program just hangs whenever i try to read, when i use fgets, it reads the whole line and stores the integers from the photo reference array into my name array (middle one). I am wondering how I should go about doing this, I've spent days trying to figure out a solution but nothing seems to work. This is what my text file looks like:
123456-1234 Name Name [1, 2, 3, 4]
234567-2345 Name2 Name2 [1, 2]
345678-3456 Name3 Name3 []
And this is my write_to_file function which writes to the file when the program exits:
void write_to_file(Patient reg[], int *pNr_of_patients){
FILE *fp;
fp=fopen("file.txt","w");
if(*pNr_of_patients>0){
int i,j;
for(i=0;i<*pNr_of_patients;i++){
fprintf(fp,"%s\t%s\t[",reg[i].pers_nr,reg[i].name);
for(j=0;j<reg[i].nr_of_ref-1;j++){
fprintf(fp,"%d, ",reg[i].photo_ref[j]);
}
if(reg[i].photo_ref[j]==0){
fprintf(fp,"]");
}else{
fprintf(fp,"%d]",reg[i].photo_ref[j]);
}
fprintf(fp,"\n");
}
fclose(fp);
}
}
This is my read_from_file function, it's missing code for reading the int array values at the end:
Edit: I added a for loop to remove the characters starting at "[" from the name string, now i just need to know how to read the array values at the end into the struct's photo reference array.
void read_from_file(Patient reg[],int *pNr_of_patients){
FILE *fp;
fp=fopen("file.txt","r");
if(fp!=NULL){
reg[*pNr_of_patients].nr_of_ref=0;
int i=0, pos;
while(fgets(reg[*pNr_of_patients].pers_nr,13,fp)!=NULL){
reg[*pNr_of_patients].pers_nr[strlen(reg[*pNr_of_patients].pers_nr)-1]='\0';
fgets(reg[*pNr_of_patients].name,31,fp);
reg[*pNr_of_patients].name[strlen(reg[*pNr_of_patients].name)-1]='\0';
for(pos=0;pos<30;pos++){
if(reg[*pNr_of_patients].name[pos]=='['){
reg[*pNr_of_patients].name[pos]='\0';
}
}
(*pNr_of_patients)++;
}
fclose(fp);
}else{
printf("File does not exist\n");
}
}
This is what my Patient struct looks like:
struct patient{
char pers_nr[12], name[30];
int photo_ref[10], nr_of_ref;
};
typedef struct patient Patient;
Calling read_from_file in main:
int main(void){
Patient patient_register[10000];
int nr_of_patients=0;
read_from_file(patient_register,&nr_of_patients);
database_management(patient_register,&nr_of_patients); //this is where I fill all the data into the array before writing to the file at the end
write_to_file(patient_register,&nr_of_patients);
return 0;
}
I think that scanning input is one of the hardest in C. That's why libraries like cs50 exists, to ease up reading input for new C users. Anyway, I constructed my solution, but I redesigned your function.
The first solution reads a single Patient from a line. It does not use sscanf the only standard call that set's errno is to strtol, which is used to convert up numbers.
The second function uses sscanf and some crazy format string construction to stay safe of buffer overflow.
It all brings down at to how the input stream is constructed and how much you trust it.
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
struct patient{
char pers_nr[12];
char name[30];
int photo_ref[10];
size_t nr_of_ref;
};
typedef struct patient Patient;
int patient_read_from_line_1(const char line[], Patient *p)
{
assert(line != NULL);
assert(p != NULL);
// check the first 12 characters ----------
// first 6 chars must be numbers
for (int i = 0; i < 6; ++i) {
if (!isdigit(line[i])) {
return -__LINE__;
}
}
// followed by a single '-'
if (line[6] != '-') {
return -__LINE__;
}
// followed by 4 numbers
for (int i = 7; i < 7 + 4; ++i) {
if (!isdigit(line[i])) {
return -__LINE__;
}
}
// followed by a space
if (line[7 + 4] != ' ') {
return -__LINE__;
}
// read up first field ---------------------
// cool first field checks out
memcpy(p->pers_nr, line, 11);
p->pers_nr[11] = '\0';
line += 12;
// let's omit spaces
while (line[0] == ' ') {
line++;
}
// read up second field --------------------------
// now we should read a two strings separated by a space
// so we should read up until a second space
if (!isalpha(*line)) {
return -__LINE__;
}
const char *pnt_first_space = strchr(line, ' ');
if (pnt_first_space == NULL) {
return -__LINE__;
}
const char *pnt_another_space = strchr(pnt_first_space + 1, ' ');
if (pnt_another_space == NULL) {
return -__LINE__;
}
const size_t name_to_read_length = pnt_another_space - line;
if (name_to_read_length > sizeof(p->name)) {
return -__LINE__;
}
memcpy(p->name, line, name_to_read_length);
p->name[name_to_read_length] = '\0';
// buh two fields done, now the array
line += name_to_read_length;
// let's omit the spaces
while (line[0] == ' ') {
line++;
}
// read up array -----------------------------------
// array
if (line[0] != '[') {
return -__LINE__;
}
line++;
for (size_t numscnt = 0;; ++numscnt) {
if (numscnt >= sizeof(p->photo_ref)/sizeof(*p->photo_ref)) {
return -__LINE__;
}
char *pnt;
errno = 0;
long num = strtol(line, &pnt, 10);
if (errno) {
return -__LINE__;
}
if (!(INT_MIN < num && num < INT_MAX)) {
return -__LINE__;
}
p->photo_ref[numscnt] = num;
line = pnt;
// omit spaces
while (*line == ' ') line++;
// now we should get a comma
if (line[0] != ',') {
// if don't get a comma, we need to get a ]
if (line[0] == ']') {
// cool
++line;
// but remember to save the count
p->nr_of_ref = numscnt + 1;
// cool
break;
}
return -__LINE__;
}
++line;
// omit spaces
while (*line == ' ') line++;
// start again
}
// this needs to be end of line or newline
if (line[0] != '\0' && line[0] != '\n') {
return -__LINE__;
}
// success!
return 0;
}
// ok, ok, ok, let's use sscanf
int patient_read_from_line_2(const char line[], Patient *p)
{
assert(line != NULL);
assert(p != NULL);
int ret;
int pos;
// read up first fiedl and half of the second ------------------
ret = sscanf(line, "%12s %30[^ ] %n", p->pers_nr, p->name, &pos);
if (ret != 2) {
return -__LINE__;
}
line += pos;
// read up another half of the second field -------------------
const size_t cur_name_len = strlen(p->name);
p->name[cur_name_len] = ' ';
char tmp[20];
ret = snprintf(tmp, 20, "%%%d[^ ] [%%n", (int)(sizeof(p->name) - cur_name_len - 1));
if (ret < 0) {
return -__LINE__;
}
ret = sscanf(line, tmp, &p->name[cur_name_len + 1], &pos);
if (ret != 1) {
return -__LINE__;
}
line += pos;
// read up array *sigh* -------------------------------------------
for (p->nr_of_ref = 0;; ++p->nr_of_ref) {
if (p->nr_of_ref >= sizeof(p->photo_ref)/sizeof(*p->photo_ref)) {
return -__LINE__;
}
ret = sscanf(line, " %d%1s%n", &p->photo_ref[p->nr_of_ref], tmp, &pos);
if (ret == 0) {
// hm...
if (line[0] == ']') {
// ach all ok, empty numbers list;
line++;
p->nr_of_ref++;
break;
}
return -__LINE__;
}
if (ret != 2) {
return -__LINE__;
}
line += pos;
if (tmp[0] != ',') {
if (tmp[0] == ']') {
// whoa! success
p->nr_of_ref++;
// cool
break;
}
return -__LINE__;
}
}
// so what's left? - EOF or newline
if (line[0] != '\0' && line[0] != '\n') {
return -__LINE__;
}
// success!
return 0;
}
long patient_read_from_file(FILE *fp, Patient patients[], size_t patients_len)
{
size_t patients_cnt = 0;
char line[256];
// for each line in file
while (fgets(line, sizeof(line), fp) != NULL) {
const int ret = patient_read_from_line_2(line, &patients[patients_cnt]);
if (ret < 0) {
// hanle reading error
return ret;
}
patients_cnt++;
if (patients_cnt > patients_len) {
// no more memory in patients left
return -__LINE__;
}
}
return patients_cnt;
}
void patient_fprintln(FILE *f, const Patient *p)
{
fprintf(f, "%s %s [", p->pers_nr, p->name);
for (size_t i = 0; i < p->nr_of_ref; ++i) {
fprintf(f, "%d", p->photo_ref[i]);
if (i + 1 != p->nr_of_ref) {
fprintf(f, ",");
}
}
fprintf(f, "]\n");
}
int main()
{
FILE *fp;
fp = stdin; // fopen("file.txt","r");
if (fp == NULL) {
return -__LINE__;
}
Patient patients[3];
const long patients_cnt = patient_read_from_file(fp, patients, sizeof(patients)/sizeof(*patients));
if (patients_cnt < 0) {
fprintf(stderr, "patient_read_from_file error %ld\n", patients_cnt);
return patients_cnt;
}
fclose(fp);
printf("Readed %d patients:\n", patients_cnt);
for (size_t i = 0; i < patients_cnt; ++i) {
patient_fprintln(stdout, &patients[i]);
}
return 0;
}
Live version available at onlinedbg.
This can be simplified for 100%. This has bugs for 100%. It is just to show what methods (strtol, memcpy, sscanf, isdigit, isalpha) are sometimes used by people to read from input. Also I specify length modifier to scanf (sscanf(..., "%12s") to handle overflows (hopefully). Try to always check return values from scanf and other standard functions (maybe checking snprintf return value is a little too much, but hey, let's be consistent). Be vary, that on some platforms the %n scanf modifier happens not to work. Also this can be build up to use dynamic allocation using malloc, realloc and free, both on line reading (basically it is equal to writing custom version of GNU getline), reading strings from input, reading int's array from input and dynamic allocations of patients.
This was meant as a comment but got too long, so I type it here.
read_from_file() appears overly complex. You might consider revisiting fscanf, reading the photo references as a whole string and then parsing into integers which you can assign to the photo_ref array. (While the code below might compile, I haven't verified that it works. It's just an idea of how one might proceed.)
void read_from_file (Patient reg[], int *pNr_of_patients)
{
FILE *fp;
fp = fopen ("file.txt", "r");
if (fp != NULL)
{
int n;
int i = 0; // position in photo_ref
char refs[30];
*pNr_of_patients = 0;
while (EOF !=
(n =
fscanf (fp, "%s %[^[]%[^]]]", reg[*pNr_of_patients].pers_nr,
reg[*pNr_of_patients].name, refs)))
{
// btw, reg[*pNr_of_patients].name may contain terminating blanks. right trim it. that's easy enough.
if (n > 2)
{ /* found photo refs.Now split the string into integers */
char *s = refs + 1; //skip '['
char *p;
while (*s && i<10){ // scan for the integers, 10 of them
while (*s && *s == ' ')
s++; // skip blanks
p = s; // mark start of number
while (*p && *p != ',')
p++;
if (*p == ',')
*p = 0;
reg[*pNr_of_patients].photo_ref[i++] = atoi (s); //tip: use strtol(3), verify that `i' isnt larger than size of the array
s = p + 1; // skip ','. Must Do: verify that `s' hasnt yet moved past the end of `ref'!!
}
}
(*pNr_of_patients)++;
}
fclose (fp);
}
else
{
printf ("File does not exist\n");
}
}
There are some good answers already, but most of them try to use a single method to parse all elements of the line. I would read whole lines into a buffer first, then use sscanf() to parse the patient number and name, but use strtok() to split the array into its individual components:
void read_from_file(Patient reg[], int *pNr_of_patients) {
FILE *fp = fopen("file.txt", "r");
if (!fp) {
fprintf(stderr, "Error opening file: %s\n", strerror(errno));
*pNr_of_patients = 0;
return;
}
char line[1024];
int i = 0;
while (fgets(line, sizeof line, fp)) {
int offset = 0;
int refs = 0;
sscanf(line, "%11s %29[^[] [%n", &reg[i].pers_nr, &reg[i].name, &offset);
for (char *tok = strtok(line + offset, ","); tok && refs < 10; tok = strtok(NULL, ",")) {
if (*tok != ']')
reg[i].photo_ref[refs++] = atoi(tok);
}
reg[i].nr_of_ref = refs;
i++;
}
*pNr_of_patients = i;
}
Divide and Conquer
Break this down into steps. Make a function that populates 1 Patient.
The below is untested code. Consider it a starting point. The deign goal is to make a function that reads 1 line into 1 Patient.
Read in 1 entire line
// return 1: success, 0: failure EOF:end-of-file
int read_once_from_file(FILE *stream, Patient *pat_ptr) {
Patient pat = { 0 };
char buffer[100 + 30*13];
if (fgets(buffer, sizeof buffer, stream) == NULL) {
return EOF;
}
Parse the first part. Use "%n" which records the parsing offset. Use width limits on string input.
int n = 0;
if (sscanf(buffer, " %11[^\t] %29[^\t] [ %n", pat.pers_nr, pat.name) != 2) {
return 0; // improper formatted input
}
char *p = buffer + n;
Now look for ']' and photo_ref
if (*p != ']') {
for (pat.nr_of_ref=0; ; pat.nr_of_ref++) {
if (sscanf(p, "%d %n", &pat.photo_ref[i], &n) != 1) {
return 0; // improper formatted input
}
p += n;
if (*p == ']') {
pat.nr_of_ref++;
break;
}
if (*p != ',' || pat.nr_of_ref + 1 == 10) {
return 0; // improper formatted input
}
p++;
}
}
Save result
*pat_ptr = pat;
return 1;
}
Call read_once_from_file() as needed
void read_from_file(Patient reg[],int *pNr_of_patients){
*pNr_of_patients = 0;
FILE *fp = fopen("file.txt","r");
if(fp){
for (int i = 0; i<10000; i++) {
int count = read_once_from_file(fp, &reg[i]);
if (count == EOF) {
break;
}
if (count != 1) {
// error
fprintf(stderr, "Input error\n");
break;
}
}
*pNr_of_patients = i;
fclose(fp);
}
}

C: assigning 2 dimensional array to another array

int main() {
FILE *fp = fopen("fileA.txt", "r"); /* read file */
int i = 0;
char name[200][100];
char goods[200][100];
char qty[200][100];
char temp[200][100];
int x = 0;
int result;
while (!feof(fp)) {
fscanf(fp, "%[^,] , %[^,] , %s " , name[i], item[i], qty[i]); /*get file content and store in array */
if (strcmp(item[i], "Football") == 0) { /* only select Football */
temp[x][x] = qty[i];
if (x > 0) {
if (strcmp(temp[x][x], temp[x + 1][x + 1]) > 0) { /*compare who has more football qty */
result = x; /*output the person who have more football*/
}
}
x = x + 1;
}
}
printf("%s is team leader in class.\n", name[result]);
fclose(fp);
getchar();
return 0;
}
Hi all, I don't know why the result not correct.
I want to find out who has more football and print out his/her name.
Seems something wrong on if (strcmp(temp[x], temp[x + 1]) > 0)
I am not clearly on using pointer and address.
the content in the text file are:
Alice,Eating,001
Kitty,Football,006
Ben,Swimming,003
May,Football,004
And I expect the result is :
Kitty is team leader in class.
Thank you.
There are multiple problems in your code:
you do not test if the file is properly open.
you cannot properly parse a file with while (!feof(fp)) {. You should iterate for as long as fscanf() returns 3, or preferably read the input line by line and parse it with sscanf().
you do not tell fscanf() the maximum number of characters to store into the destination arrays. This may cause undefined behavior for invalid input.
you do not increment i for each line read. Every line of input overwrites the previous one.
you do not check if there are more than 200 lines. Undefined behavior in this case.
Your test to find the football fan with the highest quantity is broken: no need for a 2D array here, just keep track of the current maximum and update it when needed.
Here is a modified version:
#include <stdio.h>
int main() {
FILE *fp = fopen("fileA.txt", "r"); /* read file */
char buf[300];
char name[200][100];
char goods[200][100];
char qty[200][100];
int i, qty, max_qty = 0, result = -1;
if (fp == NULL) {
fprintf(stderr, "cannot open file\n");
return 1;
}
for (i = 0; i < 200; i++) {
if (!fgets(buf, sizeof buf, fp))
break;
if (sscanf(buf, " %99[^,], %99[^,],%99s", name[i], item[i], qty[i]) != 3) {
fprintf(stderr, "invalid input: %s\n", buf);
break;
}
if (strcmp(item[i], "Football") == 0) { /* only select Football */
qty = atoi(qty[i]);
if (result == -1 || qty > max_qty) {
result = i; /*store the index of the person who have more football */
}
}
}
if (result < 0)
printf("no Football fan at all!\n");
else
printf("%s is team leader in class with %d in Football.\n", name[result], max_qty);
fclose(fp);
getchar();
return 0;
}
Above Code is not clear as what you want to do in this code block
if ( strcmp(temp [x], temp [x+1]) > 0 ){ /* when matches, accessing temp[x+1] results in undefined behaviour */
result = x;
}
also why char *temp[200][100]; as to store qty[i], char *temp is enough or you can take char temp[200][100];
Here is somewhat better one as requirement is not clear.
int main() {
FILE *fp= fopen("fileA.txt","r"); /* read file */
if(fp == NULL) {
/* not exist.. write something ?? */
return 0;
}
char name [200][100],goods[200][100],qty[200][100],temp[200][100];
int x = 0,result = 0, i = 0;
while ((fscanf(fp, "%[^,] , %[^,] , %s " , name[i], goods [i], qty[i])) == 3) {
if (strcmp(goods[i] , "Football") == 0){
strcpy(temp[x],qty[i]);
if ( strcmp(temp [x], temp [x+1]) > 0 ) { /* UB ? */
result = x;
x+=1;
}
}
}
printf("%s is team leader in class. \n", name[result]);
fclose(fp);
getchar();
return 0;
}

C Program Segmentation Fault main()

I am novice to C programming and I have written a code to a requirement specification but I am consistently getting Segmentation Fault and unable to proceed ahead.
If the file name is 'code.c' and it runs with an error of not passing the argument (filename). But if the filename is passed, we land in Segmentation Fault.
Any help/suggestions will be appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
struct _data
{
char *firstName;
char *lastName;
long number;
};
// SCAN FILE
int SCAN(FILE *(*stream))
{
*stream = fopen("inputFile.data", "r");
int ch = 0, lines = 0;
while (!feof(*stream))
{
ch = fgetc(*stream);
if (ch == '\n')
{
lines++;
}
}
return lines;
}
// LOAD FILE
struct _data *LOAD(FILE *stream, int size)
{
int i;
size_t chrCount;
char *text, *number, *firstName, *lastName;
struct _data *BlackBox;
if ((BlackBox = (struct _data*)calloc(size, sizeof(struct _data))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
rewind(stream);
for (i = 0; i < size; i++)
{
getline(&text, &chrCount, stream);
firstName = strtok(text, " ");
lastName = strtok(text, " ");
number = strtok(NULL, "\n");
// Allocate memory for name part of struct.
if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName), sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName), sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
strcpy(BlackBox[i].firstName, firstName);
strcpy(BlackBox[i].lastName, lastName);
BlackBox[i].number = atol(number);
}
fclose(stream);
return BlackBox;
}
void SEARCH(struct _data *BlackBox, char *name, int size, int inputs)
{
int i;
int found = 0;
char *search = " ";
char *firstName;
char *lastName;
if (inputs == 2)
{
firstName = strtok(name, search);
lastName = strtok(NULL, search);
}
printf("*******************************************\n");
if (inputs == 2)
{
for (i = 0; i < size; i++)
{
if (!strcasecmp(firstName, BlackBox[i].firstName) && !strcasecmp(firstName, BlackBox[i].firstName))
{
printf("The name was found at the %d entry.\n", i);
found = 1;
break;
}
}
}
else
{
for (i = 0; i < size; i++)
{
if (!strcasecmp(firstName, BlackBox[i].firstName) || !strcasecmp(firstName, BlackBox[i].firstName))
{
printf("The name was found at the %d entry.\n", i);
found = 1;
break;
}
}
}
if (found == 0)
{
printf("The name was NOT found.\n");
}
printf("*******************************************\n");
}
// FREE MEMORY
void FREE(struct _data *BlackBox, int size)
{
int i;
for (i = 0; i < size; i++)
{
free(BlackBox[i].firstName);
free(BlackBox[i].lastName);
}
free(BlackBox);
BlackBox = NULL;
}
// MAIN
int main(int argv, char **argc)
{
int size;
FILE *stream;
struct _data *BlackBox;
// argv == 1 WORKS, Below message is printed.
if (argv == 1)
{
printf("*******************************************\n");
printf("* You must include a name to search for. *\n");
printf("*******************************************\n");
}
// argv == 2 DOES NOT WORK, Segmentation Fault.
if (argv == 2)
{
size = SCAN (&stream);
BlackBox = LOAD(stream, size);
SEARCH(BlackBox, argc[1], size, 1);
}
if (argv == 3)
{
size = SCAN(&stream);
BlackBox = LOAD(stream, size);
SEARCH(BlackBox, argc[2], size, 2);
}
return 0;
}
You have a problem in this code:
firstName = strtok(text, " ");
lastName = strtok(text, " ");
number = strtok(NULL, "\n");
...
BlackBox[i].number = atol(number);
The second strtok() call should pass NULL as its first argument. As it is, the third strtok() call is certain to return NULL because the first call modifies text in such a way that the second consumes the whole thing (when tokenizing again from the beginning, as it erroneously does). You do not test for that, however, and as a result, atol() attempts to dereference a null pointer.
Update:
Additionally, as #chqrlie and later #JamesWilkins observed, you do not allocate sufficient space for BlackBox[i].firstName and BlackBox[i].lastName, as you need room for the string terminators as well. This is an entirely separate problem that could also produce a segfault. I like #chqrlie's suggestion to switch to strdup(), but it would be sufficient to just increase each allocation by one unit.
Update 2:
Furthermore, you have an issue with this line:
getline(&text, &chrCount, stream);
You do not initialize variable text before the first call, so it contains a junk value. The function allocates a buffer only when its first argument points to a NULL pointer; otherwise it writes the line to the buffer pointed to by the pointer obtained by dereferencing the first argument. Writing to a random location in memory certainly produces undefined behavior, which in practice often manifests as a segfault.
Moreover, unless you can rely on no line of the file being longer than the first, you also need to free the text pointer at the end of each loop iteration AND reset its value to NULL, so that getline() allocates a fresh buffer on the next iteration. If you do not free it on each iteration, then you need instead to free it after the end of the loop; else you will leak memory.
Try this (though I'm using Visual Studio on Windows). I added code to check for a missing '\n' on the last line, and also allowed for a variable number of search terms. I also increased the memory allocation for strings by 1 to account for the null terminating character. I noticed you are using getline(const char*..., which I think is GNU (Linux?), so I change that to fgets() just so I could compile and test it in VS (so you can change it back if you like). I put in various null checks as well, to be safer.
#include <iostream>
using namespace std;
struct _data
{
char *firstName;
char *lastName;
long number;
};
// SCAN FILE
int SCAN(FILE *(*stream))
{
*stream = fopen("inputFile.data", "r");
if (*stream == NULL)
{
perror("Error opening file");
return 0;
}
char ch = 0, lines = 0, linesize = 0;
while ((ch = fgetc(*stream)) != EOF)
{
if (ch == '\n')
{
lines++;
linesize = 0;
}
else linesize++;
}
if (linesize > 0)
lines++; // (last line doesn't have '\n')
return lines;
}
// LOAD FILE
struct _data *LOAD(FILE *stream, int lineCount)
{
int i;
size_t chrCount = 256;
char text[256], *result, *number, *firstName, *lastName;
struct _data *BlackBox;
if ((BlackBox = (struct _data*)calloc(lineCount, sizeof(struct _data))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
else memset(BlackBox, 0, sizeof(struct _data) * lineCount); // (make sure all data members are null to begin)
rewind(stream);
for (i = 0; i < lineCount; i++)
{
result = fgets(text, chrCount, stream);
if (result == NULL)
break; // (EOF)
firstName = strtok(text, " ");
lastName = strtok(NULL, " ");
number = strtok(NULL, "\n");
// Allocate memory for name part of struct.
if ((BlackBox[i].firstName = (char*)calloc(strlen(firstName) + 1, sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
if ((BlackBox[i].lastName = (char*)calloc(strlen(lastName) + 1, sizeof(char))) == NULL)
{
printf("ERROR - Could not allocate memory.\n");
exit(0);
}
strcpy(BlackBox[i].firstName, firstName);
strcpy(BlackBox[i].lastName, lastName);
BlackBox[i].number = atol(number);
}
fclose(stream);
return BlackBox;
}
void SEARCH(struct _data *BlackBox, char **names, int lineCount, int inputs)
{
int i, l;
int found = 0;
printf("*******************************************\n");
for (i = 0; i < inputs; ++i)
{
for (l = 0; l < lineCount; ++l)
{
if (BlackBox[l].firstName != NULL && !_stricmp(names[i], BlackBox[l].firstName)
|| BlackBox[l].lastName != NULL && !_stricmp(names[i], BlackBox[l].lastName))
{
printf("The name was found on line %d.\n", 1 + l);
found = 1;
break;
}
}
if (found) break;
}
if (!found)
printf("The name was NOT found.\n");
printf("*******************************************\n");
}
// FREE MEMORY
void FREE(struct _data *BlackBox, int lineCount)
{
int i;
for (i = 0; i < lineCount; i++)
{
if (BlackBox[i].firstName != NULL)
free(BlackBox[i].firstName);
if (BlackBox[i].lastName != NULL)
free(BlackBox[i].lastName);
}
free(BlackBox);
}
// MAIN
int main(int argc, char **argv)
{
int lineCount;
FILE *stream;
struct _data *BlackBox;
// argc == 1 WORKS, Below message is printed.
if (argc == 1)
{
printf("*******************************************\n");
printf("* You must include a name to search for. *\n");
printf("*******************************************\n");
}
// argc == 2 DOES NOT WORK, Segmentation Fault.
if (argc > 1)
{
lineCount = SCAN(&stream);
if (lineCount > 0)
{
BlackBox = LOAD(stream, lineCount);
SEARCH(BlackBox, argv + 1, lineCount, argc - 1);
FREE(BlackBox, lineCount);
}
}
return 0;
}
Tested it on the command line, and it works.
The problem is the argv and argc. argc is supposed to be an int (think argument count), while argv is meant to be char**. You have them mixed up in your main.

(C code) how would I pass my global variables between functions and return them when the main function needs them also?

(C code) how would I pass my global variables between functions and return them when the main function needs them also? I've posted my code below for reference. Of course, I also have a header file with my function prototypes in it as well but they only list variable types inside the closed brackets, not the variable names...
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "myheader.h"
char user_filename[150];
char user_filename2[150];
FILE *fp;
FILE *fp2;
int num_shift;
int main()
{
int choice; // main variables
int option;
char result;
char ch;
int offset;
char character;
int tmp;
option = 0;
num_shift = 0;
strncpy(user_filename, "not set", sizeof("not set"));
strncpy(user_filename2, "not set", sizeof("not set"));
fp = NULL;
fp2 = NULL;
choice = menu(num_shift, user_filename, option); // get user's first selection
while(choice != QUIT) //execute so long as choice is not equal to QUIT
{
switch(choice)
{
case INPUT_FILE:
input(user_filename);
break;
case OUTPUT_FILE:
output();
break;
case NUM_TO_SHIFT:
num_shift = shift(num_shift);
printf ("Shift by %d\n",num_shift);
break;
case ENCODE:
encode(result, ch, num_shift, character);
break;
case QUIT:
quit();
break;
case REVIEW:
review (user_filename);
break;
default:
printf("Oops! An invalid choice slipped through. ");
printf("Please try again.\n");
}
choice = menu(num_shift, user_filename, 0); /* get user's subsequent selections */
}
quit();
}
int menu(int num_shift, char * user_filename, int option)
{
printf("\nText Encoder Service\n\n");
printf("1.\tEnter name of input file (currently '%s')\n", user_filename);
printf("2.\tEnter name of output file (currently '%s')\n", user_filename2);
printf("3.\tEnter number of characters data should be shifted (currently %d)\n", num_shift);
printf("4.\tEncode the text\n");
printf("5.\tReview the text in the input file\n");
printf("\n0.\tQuit\n\n");
printf("Make your selection: \n");
while( (scanf(" %d", &option) != 1) /* non-numeric input */
|| (option < 0) /* number too small */
|| (option > 5)) /* number too large */
{
fflush(stdin); /* clear bad data from buffer */
printf("That selection isn't valid. Please try again.\n\n");
printf("Your choice? ");
}
printf("Selecting %d\n\n", option);
return option;
}
int input(char * user_filename)
{
printf("Enter the filename of the file to encode:\n");
printf("(hit the Enter key when done)\n");
scanf("%s", user_filename);
printf("Getting %s\n\n", user_filename);
fp = fopen (user_filename, "r");
if (fp == NULL)
{
printf("\nSorry, I'm unable to open the file (%s) for reading\n", user_filename);
printf("Please try again.\n");
}
else
{
fclose(fp);
}
return INPUT_FILE;
}
int output()
{
printf("Enter the filename of the output file to store encoded information:\n");
printf("(hit the Enter key when done)\n");
scanf("%s", user_filename2);
printf("Opening File for Writing %s\n\n", user_filename2);
fp2 = fopen (user_filename2, "w");
if (fp2 == NULL)
{
printf("\nSorry, I'm unable to open the file (%s) for writing\n", user_filename2);
printf("Please try again.\n");
} else
{
fclose(fp2);
}
//return user_filename;
return INPUT_FILE;
}
int shift(int num_shift)
{
printf("Enter the number of letters to shift for each character: \n");
printf("(hit the Enter key when done)\n");
scanf("%d", &num_shift);
printf("Setting shift value to: %d\n\n", num_shift);
return num_shift;
}
int encode(char result, char ch, int offset, char character2)
{
int character;
printf("starting encoding with offset of %d\n", offset);
fp = fopen(user_filename, "r");
fp2 = fopen(user_filename2, "w+bc");
if ((fp == NULL) || (fp2 == NULL))
{
printf ("File not found\n");
return (0);
}
fseek(fp, 0, SEEK_SET);
printf("staring Encoding from %s to %s at position %ld\n", user_filename, user_filename2, ftell(fp));
int i = 0;
while(character = fgetc(fp))
{
if ( character == EOF)
{
//printf("%c",character);
//fprintf(fp2,"%c",result);
fclose(fp);
fflush(fp2);
fclose(fp2);
return(0);
}
if (isalpha (character))
{
if (character >= 'a' && character <= 'z')
{
result = character - 'a';
result = (result + offset) % 26; // 26 letters in the alphabet
result += 'a';
if (result < 'a')
{
result = 'z' - ('a' - result)+1;
}
} else if (character >= 'A' && character <= 'Z')
{
result = character - 'A';
result = (result + offset) % 26; // 26 letters in the alphabet
result += 'A';
if (result < 'A')
{
result = 'Z' - ('A' - result)+1;
}
}
//printf("(%c)",result);
} else
{
result = character;
//printf("(%x)", result);
}
printf("%c",result);
fprintf(fp2,"%c",result);
}
return 0;
}
void quit()
{
//fclose(fp);
//fclose(fp2);
printf("Quiting...Bye!");
printf("\n");
exit(0);
}
int review(char * user_filename)
{
char character;
fp = fopen(user_filename, "r");
printf("Showing text from %s file\n", user_filename);
printf("----------BEGIN OF TEXT--------------\n");
while(character = fgetc(fp))
{
if ( character == EOF)
{
printf("%c",character);
printf("\n----------END OF TEXT--------------\n");
fclose(fp);
return(0);
}
printf("%c",character);
}
}
You don't need to pass them around as parameters, you can just access them from anywhere (hence global, well as long as you can see the variable).
Any modifications made to those variables are visible to everyone (aside from multithreading issues) so you have no trouble using them in your functions and in main as well.
You do not need to pass global variables because global variables have global scope, that is they can be accessed anywhere. This is VERY BAD programming practice because it may introduce side-effects later in the program when you decide to use the same name for another purpose for example.
See wikipedia for details.

Resources