Related
I am doing the Advent of Code, and I am trying to do it all in C. I am currently on day three, and I kind of solved it, but as the title says it behaves very strangely in my IDE CLion. Here is the objective.
I would very much like to know why it is not running properly, and finding out why appears to be beyond my capability.
This is my code:
//
// Created by gusta on 2022-12-06.
//
#include "aocio.h"
#include <string.h>
int cexists(char* cp, char c, int length);
char getDuplicate(char* line);
int main() {
FILE* fptr = openFile("../Inputs/day3.txt");
char* line;
while (readLine(fptr, &line)) {
char c = getDuplicate(line);
putchar(c);
}
return 0;
}
char getDuplicate(char* line) { // Returnerar STRING_END om ingen fanns
unsigned int length = strlen(line);
char letters[length/2];
char* cp = &letters[0];
for (int i = 0; i < length/2; i++) {
letters[i] = ' ';
}
for (int index = 0; line[index] != STRING_END; index++) {
if (index < length/2) {
int i_char = cexists(letters, line[index], length/2);
if (i_char == -1) {
*cp = line[index];
cp++;
}
}
else {
if (cexists(letters, line[index], length/2) != -1) {
return line[index];
}
}
}
}
int cexists(char* cp, char c, int length) {
for (int i = 0; i < length; i++) {
if (cp[i] == c) {
return i;
}
}
return -1;
}
Here is aocoi.h (advent of code input output.h):
//
// Created by gusta on 2022-12-01.
//
#ifndef ADVENTOFCODE_AOCIO_H
#define ADVENTOFCODE_AOCIO_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define SIGN_ASCII 45
#define TRUE 1
#define FALSE 0
#endif //ADVENTOFCODE_AOCIO_H
#define STRING_END '\0'
#define NUMBERS_ASCII 48
char* prompt(const char* question) {
printf(question);
char* string = malloc(1);
int curChar = 0, index = 0;
while (curChar != '\n') {
curChar = getchar();
if (curChar == '\n'){
string[index] = STRING_END;
}
else{
if (index > 0) {
string = (char*) realloc(string, index+1);
}
string[index] = curChar;
}
index++;
}
return string;
}
FILE* openFile(const char* fileName) {
FILE *fptr;
fptr = fopen(fileName, "r");
if (fptr == NULL) {
printf("Big fat file error!!\n");
fclose(fptr);
getchar();
exit(-1);
}
return fptr;
}
char readLine(FILE* fptr, char** line) {
int index = 0, end = 0;
char* string = (char *) malloc(1);
char curChar;
do {
end = fscanf(fptr, "%c", &curChar);
if (end == EOF) {
string[index] = STRING_END;
fclose(fptr);
*line = string;
return FALSE;
}
if (curChar == '\n') {
string[index] = STRING_END;
*line = string;
return TRUE;
}
else {
if (index > 0) {
string = (char *) realloc(string, index + 1);
}
string[index] = curChar;
index++;
}
} while (end != EOF);
}
int parseToInt(char* string) {
int numberLength = 0, number = 0;
int sign = FALSE;
for (int index = 0; string[index] != STRING_END; index++) {
numberLength++;
}
for (int index = numberLength-1; index >= 0; index--) {
char curChar = string[index];
if (index == 0 && curChar - SIGN_ASCII == 0) {
sign = TRUE;
continue;
}
if (curChar - NUMBERS_ASCII >= 0 && curChar - NUMBERS_ASCII <= 9) {
int num = (int) (curChar - NUMBERS_ASCII);
num *= (int)(pow(10, numberLength-index-1));
number += num;
}
else {
printf("Felaktig inmatning. parseInt kan bara ta in tal"); // Invalid input. parseInt can only take in numbers
getchar();
exit(-1);
}
}
if (sign == TRUE) {
number *= -1;
}
return number;
}
Through searching the web I found that the error code should mean stack overflow (ha!) or something like that, but I cannot for the life of me find any place in my code where that could occur. All pointers and stuff should be automatically freed - is there something I have missed?
In readLine():
if (index > 0) {
string = (char *) realloc(string, index + 1);
}
string[index] = curChar;
After this section, the buffer has size index+1, therefore string[index] is the last element you can write to. Afterwards, you do
index++;
Now, writing to string[index] is out of bounds, resulting in a buffer overflow. This is what happens when an EOF or EOL is detected.
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;
}
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", ®[i].pers_nr, ®[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, ®[i]);
if (count == EOF) {
break;
}
if (count != 1) {
// error
fprintf(stderr, "Input error\n");
break;
}
}
*pNr_of_patients = i;
fclose(fp);
}
}
i have just built a program that reads a data from a file named "text.txt" and parse it through JSON as i know the size of a file that im reading character wise so in for loop i have defined the value of i in for loop as 647, pleas help me if the value increases or decreases then how would i manage it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "jsmn.h"
void error(char *msg)
{
perror(msg);
exit(0);
}
static int jsoneq(const char *json, jsmntok_t *tok, const char *s)
{
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && strncmp(json + tok->start, s, tok->end - tok->start) == 0)
{
return 0;
}
return -1;
}
int main(int argc, char const *argv[])
{
int i,n,r;
char buf[1024];
char JSON_STRING[5000];
jsmn_parser p;
jsmntok_t t[2048];
char * fname;
FILE *fp=fopen("text.txt","r+");
FILE *ff;
if(fp==NULL)
{
error("file opening error");
}
for(int i=0; i<647;i++) /////////this if the for loop //////////
{
JSON_STRING[i] = getc(fp);
}
jsmn_init(&p);
r = jsmn_parse(&p,JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0]));
if (r < 0){
printf("\nFailed to parse JSON: %d\n", r);
return 1;
}
else{
for (i = 1; i < r; i++){
if (jsoneq(JSON_STRING, &t[i], "RID") == 0)
// for extracting the value of Rid from the complete string
{/* We may use strndup() to fetch string value */
//printf("RID: '%.*s'\n",t[i+1].end - t[i+1].start, JSON_STRING+ t[i+1].start);
printf("- RID: %.*s\n", t[i+1].end-t[i+1].start,JSON_STRING + t[i+1].start);
sprintf(fname,"%.*s",t[i+1].end - t[i+1].start, JSON_STRING+ t[i+1].start);
ff=fopen (fname, "w");
fprintf(ff, "RID: '%.*s'\n",t[i+1].end - t[i+1].start, JSON_STRING+ t[i+1].start );
i++;
}
}
else if (jsoneq(JSON_STRING, &t[i], "DID") == 0) //for Extracting the value of DID from te string recived from the client JSON_STrING
{
/* We may additionally check if the value is either "true" or "false"*/
printf("- DID: %.*s\n", t[i+1].end-t[i+1].start,JSON_STRING + t[i+1].start);
fprintf(ff, "DID: '%.*s'\n",t[i+1].end - t[i+1].start, JSON_STRING+ t[i+1].start );
i++;
//sprintf(fname,"%s_%.*s",fname,t[i+1].end - t[i+1].start, JSON_STRING+ t[i+1].start);
}
else if (jsoneq(JSON_STRING, &t[i],"TS") == 0)
{
/* We may want to do strtol() here to get numeric value */
printf("- TS: %.*s\n", t[i+1].end-t[i+1].start,JSON_STRING + t[i+1].start);
fprintf(ff, "TS: '%.*s'\n",t[i+1].end - t[i+1].start, JSON_STRING+ t[i+1].start );
// sprintf(fname,"%s_%.*s",fname,t[i+1].end - t[i+1].start, JSON_STRING+ t[i+1].start);
}
/* else
{
printf("Unexpected key: %.*s\n", t[i].end-t[i].start,JSON_STRING + t[i].start);}*/
}
printf("JSON parsed : %d\n", r);
printf("output have been generated to the file");
fprintf(ff, "%s\n",JSON_STRING );
fclose(ff);
}
return EXIT_SUCCESS;
fclose(fp);
return 0;
}
You need first to compute the amount of characters in the file, which will be the size of your json buffer
//computing how much data is needed
fseek(fp, 0L, SEEK_END);
long int json_size = ftell(fp);
rewind(fp);
Then read the file till you reach the end of the buffer or till the value read is EOF. With the code above both these conditions are the same, but under other conditions you must use both tests. Which give
JSON_STRING = (char*)malloc(json_size+1 * sizeof(char));
JSON_STRING[json_size] = '\0';
//reading the value
int val = 0;
int i = 0;
while(i < json_size && (val = getc(fp)) != EOF)
{
JSON_STRING[i] = val;
++i;
}
I am implementing a program which hides messages in ppm files and then retrieves them. One of the last things I need to do is to "return 2" if there is a problem opening the file. If I enter a file which can't be opened (file doesn't exist or wrong extension, etc) then it will display the error message on stderr, but for some reason it won't execute the very next line, "return 2"!
It simply then returns 0 instead of returning 2.
int load_ppm_image(const char *input_file_name, unsigned char **data, int *n,
int *width, int *height, int *max_color)
{
char line[80];
char c;
FILE * image_file;
//image_file = fopen(input_file_name, "rb");
if (fopen(input_file_name, "rb") == NULL) // checks to see if file cant be opened
{
fprintf(stderr, "The input image file could not be opened\n");
return 2; // why isn't this being returned???
}
else
{
image_file = fopen(input_file_name, "rb");
}
fscanf(image_file, "%s", line);
fscanf(image_file, "%d %d", width, height);
*n = (3 * (*width) * (*height));
fscanf(image_file, "%d%c", max_color, &c);
*data = (unsigned char *)malloc((*n)*sizeof(unsigned char));
size_t m = fread(*data, sizeof(unsigned char), *n, image_file);
assert(m == *n);
fclose(image_file);
return 0;
}
int hide_message(const char *input_file_name, const char *message, const char *output_file_name)
{
unsigned char * data;
int n;
int width;
int height;
int max_color;
n = 3 * width * height;
int code = load_ppm_image(input_file_name, &data, &n, &width, &height, &max_color);
if (code)
{
// return the appropriate error message if the image doesn't load correctly
return code;
}
int len_message;
int count = 0;
unsigned char letter;
// get the length of the message to be hidden
len_message = (int)strlen(message);
for(int j = 0; j < len_message; j++)
{
letter = message[j];
int mask = 0x80;
// loop through each byte
for(int k = 0; k < 8; k++)
{
if((letter & mask) == 0)
{
//set right most bit to 0
data[count] = 0xfe & data[count];
}
else
{
//set right most bit to 1
data[count] = 0x01 | data[count];
}
// shift the mask
mask = mask>>1 ;
count++;
}
}
// create the null character at the end of the message (00000000)
for(int b = 0; b < 8; b++){
data[count] = 0xfe & data[count];
count++;
}
// write a new image file with the message hidden in it
int code2 = write_ppm_image(output_file_name, data, n, width, height, max_color);
if (code2)
{
// return the appropriate error message if the image doesn't load correctly
return code2;
}
return 0;
}
Edit:
int main(int argc, const char * argv[])
{
if (argc < 2 || argv[1][0] != '-')
{
// the user didn't enter a switch
fprintf(stderr, "Usage: no switch was entered.\n");
return 1;
}
else if ((strcmp(argv[1], "-e") == 0) && (argc == 5))
{
// hides a message in an output file
hide_message(argv[2], argv[3], argv[4]);
}
else if ((strcmp(argv[1], "-d") == 0) && (argc == 3))
{
// retrieves a hidden message in a given file
retrieve_message(argv[2]);
}
else
{
// all other errors prompt a general usage error
fprintf(stderr, "Usage: error");
return 1;
}
}
Program ended with exit code: 0
The exit code of a C program is the return value of main or the status value in an explicit exit function call. In your case, the load_ppm_image returns a value to its caller function hide_message which in turn returns the same value to its caller function main. However, main does not explicitly return anything for the case that calls hide_message. The C standard specifies that main implicitly returns 0 if it reaches the end of the function without an explicit return. Hence the exit code of 0 in your case.
To get the behaviour you desire modify your main code to return the hide_message return value.
else if ((strcmp(argv[1], "-e") == 0) && (argc == 5))
{
// hides a message in an output file
return (hide_message(argv[2], argv[3], argv[4]));
}
From code :
In main function you are calling the hide_message function, but not collecting return value of hide_message()
hide_message(argv[2], argv[3], argv[4]);
So the main function is ending with return 0.