I don't get any error yet when I am trying to get value from array the program fails. The program contains a function to read products from file and store them in array of of type typedef structure item.
This is how the program looks like:
item *displayProducts(int balance){
int row=0;
char line[MAX_LINE_SIZE + 1]; // ptr to the current input line
static item products[8];
FILE *fp;
fp = fopen("machinedata.txt", "r");
if (fp == NULL)
{
printf("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
while (fgets(line, MAX_LINE_SIZE, fp)) {
char *next_ptr = NULL;
char *next_item = strtok_s(line, ",;", &next_ptr);
while (next_item != NULL){
char *item_ptr = NULL;
char *name = strtok_s(next_item, "-", &item_ptr);
if (name == NULL)
{
fprintf(stderr, "Failed to scan name out of [%s]\n", next_item);
break;
}
int price;
next_item = strtok_s(NULL, " ,", &item_ptr);
//assert(next_item != NULL);
if (strcmp(name," ")){
if (sscanf(next_item, "%d", &price) != 1)
fprintf(stderr, "Failed to convert [%s] to integer\n", next_item);
else if (balance > price){
products[row].name = name;
products[row].price = price;
products[row].product_code = row + 1;
printf("%d) %s:%d\n",products[row].product_code, products[row].name, products[row].price);
row++;
}
next_item = strtok_s(NULL, ",;", &next_ptr);
}
}
}
fclose(fp);
return products;
}
void main( int argc, char *argv[]){
int *ptr_to_balance;
int balance = atoi(argv[2]);
ptr_to_balance = &balance;
item *ptr_to_products;
Init(argv[1], balance);
ptr_to_products = displayProducts(balance);
printf("%s", *(ptr_to_products[2].name));
}
the program will print out all of the products from the file but for some reason the last line of the program fails. Any idea why?
I think, you need to change
printf("%s", *(ptr_to_products[2].name));
to
printf("%s", ptr_to_products[2].name);
as %s expects a pointer-to-null-terminated char array.
All the pointers in your products array point into the line array. This has two problems:
This array is local to displayProducts, and it's destroyed when the function returns.
Each element of products is has pointers to the same line array. So when you read a new line from the file, you're overwriting the values that were saved in the previous elements of products.
You need to make a copy of name in the heap before you save it in products[row].
char *name_copy = malloc(strlen(name)+1);
strcpy(name_copy, name);
products[row].name = name_copy;
You also need to fix the printing code, as in the other answer:
printf("%s", ptr_to_products[2].name);
Related
I'm trying to get a program to parse a text file line by line for some data storage (I've tried binary file storage, but having mixed data types in the file seems to mess things up).
I'm trying to save a number of entries contained in hist[], which is an array of the history structure which basically contains a float (.value) and a time_t (.event_Time). Before saving these entries, the number of entries should be saved (currently an int).
So far I can write the file just fine using the following function:
void data_save(int node_Id, int sensor_Id, history *hist, int entries){
FILE *file;
char data_Dir[FILENAME_MAX] = "";
char directory[FILENAME_MAX] = "";
char fileName[FILENAME_MAX] = "";
int length = 0;
//define path of the file
_getcwd(data_Dir, FILENAME_MAX);
strcat(data_Dir, "\\Data");
strcat(directory,"\\Node_");
length = snprintf(NULL, 0,"%d",node_Id);
char str1[length];
sprintf(str1, "%d", node_Id);
strcat(directory,str1);
strcat(fileName,"\\Sensor_");
length = snprintf(NULL, 0,"%d",sensor_Id);
char str2[length];
sprintf(str2, "%d", sensor_Id);
strcat(fileName,str2);
strcat(fileName,".txt");
printf("%s\n", directory);
printf("%s\n", fileName);
//check if the Data directory exists, create it if not
if (directory_exists(data_Dir) == false) {
printf("Making directory\n");
_mkdir(data_Dir);
}
strcat(data_Dir, directory);
//check if the Node directory exists, create it if not
if (directory_exists(data_Dir) == false) {
printf("Making directory\n");
_mkdir(data_Dir);
}
strcat(data_Dir, fileName);
printf("%s\n", data_Dir);
//open the file
file = fopen(data_Dir, "w");
if(file == NULL){
printf("Error while opening file.\n");
exit (1);
}
//Save the number of entries
printf("Saving %d entries\n", entries);
fprintf(file, "%d\n", entries);
//Save each entry in the inverse chronological order
//(ie. latest event first)
for(int i=entries-1; i > -1; i--){
fprintf(file, "%f %ld\n", hist[i].value, hist[i].event_Time);
}
fclose(file);
free(data_Dir);
free(directory);
free(fileName);
printf("Node %d, sensor %d: Data saved Successfully (%d Entries)\n", node_Id, sensor_Id, entries);
return;
}
However, I am getting issues when trying to load the file I've just created using the following function:
history * data_load(int node_Id, int sensor_Id, int *entries){
FILE *file;
char data_Dir[FILENAME_MAX] = "";
char directory[FILENAME_MAX] = "";
char fileName[FILENAME_MAX] = "";
int length = 0;
int entries_Temp;
int maxChar = 1000;
char stream[maxChar];
//define path of the file
_getcwd(data_Dir, FILENAME_MAX);
strcat(data_Dir, "\\Data");
strcat(directory,"\\Node_");
length = snprintf(NULL, 0,"%d",node_Id);
char str1[length];
sprintf(str1, "%d", node_Id);
strcat(directory,str1);
strcat(fileName,"\\Sensor_");
length = snprintf(NULL, 0,"%d",sensor_Id);
char str2[length];
sprintf(str2, "%d", sensor_Id);
strcat(fileName,str2);
//check if the Data directory exists, exit if not
if (directory_exists(data_Dir) == false) {
printf("//Data does not exist\n");
*entries = 0;
return NULL;
}
strcat(data_Dir, directory);
//check if the Node directory exists, exit if not
if (directory_exists(data_Dir) == false) {
printf("//Data//Node%d does not exist\n", node_Id);
*entries = 0;
return NULL;
}
strcat(data_Dir, fileName);
printf("%s\n", data_Dir);
//check if file exists (ie. there has been no previous
//data for the given sensor) exit and return 0
//existing entries
file = fopen(data_Dir, "r");
if(file!=NULL){
printf("No file found for given sensor\n");
*entries = 0;
return NULL;
}
//Read the number of entries in the file
printf("Reading number of entries\n");
printf("%s", fgets(stream, sizeof(stream), file));
printf("%s\n", stream);
*entries = strtol(stream, NULL, 10);
printf("Entries : %d\n", *entries);
if(*entries > 100){
printf("Entries is NOK\n");
exit(1);
}
//create the array of structures containing the data
printf("Creating the data array\n");
history *hist = malloc(*entries * sizeof(history));
//Read the data and copy it to the array
//this has not been tackled yet
printf("Closing file\n");
fclose(file);
printf("Freeing memory (filenames...)\n");
free(data_Dir);
free(directory);
free(fileName);
printf("Node %d, sensor %d: Data loaded Successfully (%d Entries)", node_Id, sensor_Id, *entries);
return hist;
}
From what I can gather, it seems fgets returns NULL every time. I am unsure if the file is being read correctly, but it seems that the program manages to open the file as fopen returns non NULL. I also suspect that my first attempt at this using binary files might have failed for similar reasons, but due to the format, I couldn't check if the error was occuring during writing or reading of the file.
I'd like to get some insight on why fgets is failing. I'd also appreciate any guidance on better ways to handle saving and loading data from files, as I'm only a beginner in C and I'm pretty sure there's some more optimal ways of doing what I'm trying to achieve.
At least these problems:
Off by 1.
With a short buffer, sprintf(str1, "%d", node_Id); is undefined behavior (UB) and rest of code is all suspect.
length = snprintf(NULL, 0,"%d",node_Id);
// char str1[length];
char str1[length + 1];
sprintf(str1, "%d", node_Id);
...
//char str2[length];
char str2[length+1];
Bad free
Do not call free() on something that lacks a matching *alloc().
//free(data_Dir);
//free(directory);
//free(fileName);
Suggest simplify string code.
E.g. filename
char fileName[FILENAME_MAX];
int length = snprintf(fileName, sizeof fileName, "%s%d%s",
"\\Sensor_", sensor_Id, ".txt");
if (length < 0 || length >= sizeof fileName) {
Handle_BufferTooSmall_Error();
}
else {
printf("%s\n", fileName);
}
I'm new at programming, and I need help in my C project. I have to search for a city, confirm it exists in the first file (city.csv), and take its id from there. Then I have to match that id with the corresponding one in the second file (meteo.csv), and then edit its weather information, that is in that second file. However, I don't know how I can take the city id from the first file, and then how to edit the second file after obtaining all the new weather informations. Here is the code:
void addInfo() {
FILE * fp;
char id_city[100];
char city[100];
char humidity[100];
char temp_max[100];
char temp_min[100];
char pressure[100];
char date[100];
printf("Name of the city: ");
scanf("%s", city);
// I think it's here that I have to write the code for take the city's id from the first file
if (id_city != NULL) {
printf("Maximun temperature: ");
scanf("%s", temp_max);
printf("Minimun temperature: ");
scanf("%s", temp_min);
printf("Humidity: ");
scanf("%s", humidity);
printf("Pressure: ");
scanf("%s", pressure);
printf("Date, in the format YYYY-MM-DD: ");
scanf("%s", date);
fp = fopen ("meteo.csv", "a");
fprintf(fp, "%s, %s, %s, %s, %s \n", temp_max, temp_min, humidity, pressure, date); //I think there's something wrong here too...
fclose(fp);
printf("Information edited successfully");
}
The file city.csv has 152 lines and 4 columns:
(id_city,city,county,district)
such as
(56,Lisbon,Lisbon,Lisbon)
The file meteo.csv has 152 lines and 7 columns:
(id_meteo_city,id_city,temp_max,temp_min,humidity,pressure,date)
such as
(56,56,14,5,62,1025,2018-02-12)
The first thing I would do is encapsulate the data in a struct, that makes it
easier to map a line of a CSV file into an object representing a line.
If both files city.csv and meteo.csv have different columns, I'd create a
different struct for each file. If both files have the same columns, you could
use the struct. I assume that both files are different and that city has the
format meteo_id,city_id,name.
typedef struct city_t {
int meteo_id;
int city_id;
char name[100]; // no city should have
// longer than 100 chars
} city_t;
typedef struct meteo_t {
int meteo_id;
int city_id;
int tempt_max;
int tempt_mix;
double humidity;
double preassure;
char date[11];
} meteo_t;
Let's assume that both files are well formatted, otherwise you would have to
write code that checks for errors and handles them, that would be the next step
in the exercise, so I'm going to write only the basic version with basic error
recognition.
#include <stdio.h>
#include <string.h>
#include <errno.h>
// takes 2 params, the filename and a pointer
// to size_t where the number of cities is stored
city_t *read_cities(const char *filename, size_t *len)
{
if(filename == NULL || len == NULL)
return NULL;
FILE *fp = fopen(filename, "r");
if(fp == NULL)
{
fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
return NULL;
}
city_t *arr = NULL, *tmp;
*len = 0;
// assuming that no line will be longer than 1023 chars long
char line[1024];
while(fgets(line, sizeof line, fp))
{
tmp = realloc(arr, (*len + 1) * sizeof *arr);
if(tmp == NULL)
{
fprintf(stderr, "could not parse the whole file %s\n", filename);
// returning all parsed cities so far
if(*len == 0)
{
free(arr);
arr = NULL;
}
return arr;
}
arr = tmp;
// %99[^\n] is to read up to 99 characters until the end of the line
if(sscanf(line, "%d,%d,%99[^\n]", &(arr[*len].meteo_id),
&(arr[*len].city_id), arr[*len].name) != 3)
{
fprintf(stderr, "Invalid line format (skipping line):\n%s\n", line);
// skip this line, and decrement *len
(*len)--;
continue;
}
// incrementing only when parsing of line was OK
(*len)++;
}
fclose(fp);
// file is empty or
// all lines have wrong format
if(*len == 0)
{
free(arr);
arr = NULL;
}
return arr;
}
void print_cities(city_t *cities, size_t len, FILE *fp)
{
if(cities == NULL || fp == NULL)
return;
for(size_t i = 0; i < len; ++i)
fprintf(fp, "%d,%d,%s\n", cities[i].meteo_id, cities[i].citiy_id,
cities[i].name);
}
Now I've written the read and write functions for the file citiy.csv assuming the
format meteo_id;city_id;name. The print_cities allows you to print the CSV
content on the screen (passing stdout as the last argument) or to a file
(passing a FILE object as the last argument).
You can use these functions as templates for reading and writing meteo.csv, the
idea is the same.
You can use these function as follows:
int main(void)
{
size_t cities_len;
city_t *cities = read_cities("city.csv", &cities_len);
// error
if(cities == NULL)
return 1;
do_something_with_cities(cities, cities_len);
// update csv
FILE *fp = fopen("city.csv", "w");
if(fp == NULL)
{
fprintf(stderr, "Could not open city.csv for reading: %s\n",
strerror(errno));
free(cities);
return 1;
}
print_cities(cities, cities_len, fp);
fclose(fp);
free(cities);
return 0;
}
Now for your exercise: write a similar function that parses meteo.csv (using
my function as a template shouldn't be that difficult) and parse both files. Now
that you've got them in memory, it's easy to manipulate the data (insert,
update, delete). Then write the files like I did in the example and that's it.
One last hint: how to search for a city:
// returns the index in the array or -1 on error or when not found
int search_for_city_by_name(city_t *cities, size_t len, const char *name)
{
if(cities == NULL || name == NULL)
return -1;
for(size_t i = 0; i < len; ++i)
if(strcmp(name, cities[i].name) == 0)
return i;
// not found
return -1;
}
Now I have given you almost all parts of the assignment, all you have to do is
stick them together and write the same functions for the meteo.csv file.
To edit one field:
void _ERR(char a) {
if (a == "f") printf("\n\tError File !!\n\n");
if (a == "m") printf("\n\tError Memory !!\n\n");
exit(1); }
char* stmm(const char* src) {
char* dst = malloc(strlen(src) + 1);
if (dst == NULL) return NULL;
strcpy(dst, src);
return dst; }
const char* getfield(char* line, int num) {
const char* tok;
for (tok = strtok(line, ",");
tok && *tok;
tok = strtok(NULL, ",\n"))
{
if (!--num)
return tok;
}
return NULL; }
void edit_file(char* FName, char* NewValue, int row, int col) {
int i, r = 0, c;
char line[1024];
FILE* fr, * fw;
fr = fopen(FName, "r");
fw = fopen(FName, "r+");
if (fr == NULL|| fw == NULL) _ERR("f");
while (fgets(line, 1024, fr))
{
char* tmp = stmm(line);
if (tmp == NULL) _ERR("m");
for (i = 0, c = 1; i < strlen(tmp); i++) {
if (tmp[i] == 44) c++;
}
for (i = 0; i < c; i++) {
if (r == row && i+1 == col) {
fprintf(fw,"%s", NewValue);
} else {
free(tmp);
tmp = stmm(line);
if (tmp == NULL) _ERR("m");
fprintf(fw,"%s", getfield(tmp, i + 1));
}
(i < c - 1) ? fprintf(fw,",") : fprintf(fw,"\n");
}
free(tmp);
r++;
}
fclose(fr);
fclose(fw); }
edit_file(".\FileName.csv","NewValue",Row,Column);
I have looked for an answer to my question for almost two days and tried every solution suggested to no avail.
I am trying to access a file through a linux terminal using my C Program.
I want to run popen() to do this.
The command I want to run in popen() is : grep -o %s /usr/share/dict/words
Where %s is a variable word that changes each iteration. I have tried using pointers, arrays, and alternative functions such as asprintf() / snprintf()
Here is the code I have right now:
char *message = (char *)malloc(500);
strcpy(message, "grep -n");
printf("%s", message);
strcat(message, "hello");
printf("%s", message);
strcat(message, " /usr/share/dict/words"); // SEG FAULT OCCURS HERE
printf("%s", message);
I would then pass this to popen.
I have also tried initializing as: char message[500] and this returns the same error in the same spot.
Here is my full code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "caeserheader.h"
int main( int argc, char *argv[]){
char *inputfile;
int n = 0;
int shiftamount = 0;
//Determine amount of arguments
if(argc == 2){
inputfile = argv[1];
}
else if(argc == 3){
inputfile = argv[1];
n = atoi(argv[2]);
shiftamount = n * (-1) ;
}
else{
printf("Please enter a proper number of arguments.");
return -1;
}
//OPENS INPUT FILE
FILE *input = fopen(inputfile, "r");
if(input == NULL){
printf("\n FILE NOT FOUND.");
perror("fopen");
return -1;
}
//RESERVES MEMORY AND GRABS STRING
fseek(input, 0L, SEEK_END);
long Tsize = ftell(input);
rewind(input);
char *inputtext;
inputtext = calloc( 1, Tsize+1);
//ERROR CHECKING
if(!inputtext){
fclose(input), printf("MEMORY FAILED.");
}
if(1!=fread( inputtext, Tsize, 1, input)){
fclose(input), free(inputtext), printf("READ FAIL.");
}
//CREATES DECRYPTED STRING
char newletter;
char *newstring;
int i;
//WITH GIVEN NUMBER OF SHIFTS
if(argc == 3){
newstring = malloc(Tsize + 1);
for(i=0; i<Tsize; i++){
newletter = shift(inputtext[i], shiftamount);
newstring[i] = newletter;
}
}
//WITHOUT GIVEN NUMBER OF SHIFTS
if(argc == 2){
char *message = (char *)malloc(500); //SEG FAULT SOMEWHERE HERE?
// strcpy(message, "grep -n");
// printf("%s", message);
//strcat(message, "hello");
// printf("%s", message);
// strcat(message, "/usr/share/dict/words");
//printf("%s", message);
// word = strtok(inputtext," ,.-!?\n");
// int i;
//for(i=0; i<10; i++){
//word = strtok(NULL," ,.-!?\n");
//printf("\n%s", word);
//}
// if(( fp = popen(message, "r")) == NULL){
//perror("No file stream found.");
//return -1;
// }
// else {
// pclose(fp);
// printf("FOUND.");
// }
}
// PUTS DECRYPTED STRING IN NEW FILE
char copiedname[100];
strcpy(copiedname, inputfile);
strcat(copiedname, ".dec");
FILE *newfile = fopen(copiedname, "w");
fputs(newstring, newfile);
// free(newstring);
fclose(input);
fclose(newfile);
return 0;
}
You have set inputfile to argv[1] and later you have used strcat to append to it. Don't do this. You don't own argv.
The strcat function appends a copy of the source string to the destination string, and then returns a pointer to the destination string. It does not "add two strings and return the result" which is how you seem to be using it.
I'm attempting to create a linked list using a struct from a txt file. Initially, I'm testing it with a txt file with only one line of information. This code compiles correctly, however when I run it, it returns "Line...didn't scan properly". As an aside, if I remove the if statement that returns such a value I get complete gibberish. I have no clue why the line isn't being scanned correctly, however I feel as though it may having something to do with the hyphen/plus sign in two of the terms that I tried to scan as strings. Thank you very much for any help you can provide.
This is the txt file:
1 20959U 90103A 14091.58762725 -.00000015 00000-0 00000+0 0 3197
This is the tester.c file:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct noradData {
// line one
int lineNum;
char * satNum;
char * intDesig;
float epoch;
float firstTimeDeriv;
char * secondTimeDeriv;
char * drag;
int zero;
int set;
struct noradData * next;
} Data;
Data * first = NULL, * last = NULL;
int main() {
char line[80], secondTimeDeriv[7], drag[7], satNum[6], intDesig[6];
int lineNum, zero, set;
float epoch, firstTimeDeriv;
FILE * fIn;
Data * node;
fIn = fopen("data1.txt", "r");
if (fIn == NULL) {
printf("Cannot open file\n");
return 1;
}
while (fgets(line, sizeof(line), fIn) != NULL) {
// Check line for various problems (too short, too long).
if (line[0] == '\0') {
printf ("Line too short\n");
return 1;
}
if (line[strlen (line)-1] != '\n') {
printf ("Line starting with '%s' is too long\n", line);
return 1;
}
line[strlen (line)-1] = '\0';
// Scan the individual fields.
if (scanf("%d %s %s %f %f %s %s %d %d", &lineNum, satNum, intDesig,
&epoch, &firstTimeDeriv, secondTimeDeriv, drag, &zero, &set)
!= 9) {
printf ("Line '%s' didn't scan properly\n", line);
return 1;
}
node = malloc(sizeof(Data));
if (node == NULL) {
printf ("Ran out of memory\n");
return 1;
}
node->lineNum = lineNum;
node->satNum = strdup(satNum);
node->intDesig = strdup (intDesig);
node->epoch = epoch;
node->firstTimeDeriv = firstTimeDeriv;
node->secondTimeDeriv = strdup(secondTimeDeriv);
node->drag = strdup(drag);
node->zero = zero;
node->set = set;
node->next = NULL;
if (first != NULL) {
last->next = node;
last = node;
}
else {
first = node;
last = node;
}
}
fclose (fIn);
node = first;
while (node != NULL) {
printf("%d %s %s %f %f %s %s %d %d", node->lineNum, node->satNum,
node->intDesig, node->epoch, node->firstTimeDeriv,
node->secondTimeDeriv, node->drag, node->zero, node->set);
node = node->next;
}
return 0;
}
First of all change size of character arrays satnum and intdDesig to 7
that is satnum[7] and intDesig[7]. You want to store 6 characters in these leave last index for null value.
if (line[strlen (line)-1] != '\n') {
printf ("Line starting with '%s' is too long\n", line);
return 1;
}
This if statement instead of line[strlen(line)-1]!='\n' put this-
line[strlen(line)-1]=='\n'
and statement goes like this
if (line[strlen (line)-1]=='\n') {
printf ("Line starting with '%s' is too long\n", line);
return 1;
}
And remove this line
line[strlen (line)-1] = '\0';
Then line will not be returned twice .
I have a function in which i am reading a file with products and price of each product, and i am storing products in array and prices in another array like this:
void displayProducts(int balance){
printf("-----------Available Products-----------\n");
putchar('\n');
int row=0;
const char *products[8];
int prices[8];
char line[MAX_LINE_SIZE + 1]; // ptr to the current input line
FILE *fp;
fp = fopen("machinedata.txt", "r");
if (fp == NULL)
{
printf("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
while (fgets(line, MAX_LINE_SIZE, fp)) {
char *next_ptr = NULL;
char *next_item = strtok_s(line, ",;", &next_ptr);
while (next_item != NULL){
char *item_ptr = NULL;
char *name = strtok_s(next_item, "-", &item_ptr);
if (name == NULL)
{
fprintf(stderr, "Failed to scan name out of [%s]\n", next_item);
break;
}
int price;
next_item = strtok_s(NULL, " ,", &item_ptr);
//assert(next_item != NULL);
if (strcmp(name," ")){
if (sscanf(next_item, "%d", &price) != 1)
fprintf(stderr, "Failed to convert [%s] to integer\n", next_item);
else if (balance > price){
row++;
products[row] = name;
prices[row] = price;
printf("%d) %s price %d\n", row, products[row], prices[row]);
}
next_item = strtok_s(NULL, ",;", &next_ptr);
}
}
}
}
The problem is that now i want to create a function that uses this two arrays ("Buy" function). the function will get a number and than gegt the price from the prices array and do something with it in the main() function.
how can i use the values in the prices array in a different function?
You can do it just by passing the arrays to the function and then index your item:
void buy(int * my_int_array_prices)
{
//Here, this would return you the value of the second item
//in your array: my_int_array_prices[1]
}
And you can call your function like:
buy(prices);
Since type * is a pointer as type []. type your_var[] it's a pointer to a memory zone decided at compile time, so it is a pointer.