Parsing a tab delimited text file in C - c

so, i saw an older post that showed how to do this, but for some reason it seems it no longer works, my input file consists of a header row, and then 15 rows of input, each with 5 columns, first 2 are strings, the last 3 are ints, i have a struct to store them, and im trying to write the contents into an array of these structs
FName LName Credits Transactions Modifer
John Doe 83000 2 1
Jane Doe 64000 5 3
I tried
struct customer cust = { NULL,NULL,0,0,0};
char line[SIZE] = { 0 }, * ptr = NULL;
cust.fName = malloc(15);
cust.lName = malloc(15);
printf("%d\n", &fp);
if (NULL == (fp = fopen("../customers.txt", "r")))
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
while (EOF != fscanf(fp, "%s", line))
{
ptr = strtok(line, "\\");
cust.fName = ptr;
printf("%s", ptr);
while (EOF != (ptr = strtok(NULL, "\\")))
{
i++;
printf("%s",ptr);
if (i == 1)
cust.lName = ptr;
else if (i == 2)
cust.miles = atoi(ptr);
else if (i == 3)
cust.years = atoi(ptr);
else if (i == 4)
cust.sequence = atoi(ptr);
}
i = 0;
printf("After Reading: fName:[%s] lName:[%s] miles:[%d] years:[%d]\n", cust.fName, cust.lName, cust.miles, cust.years);
}
For some reason, the printf within the inner while loop only prints "(null)", and errors out when i==2
with the error
File: minkernel\crts\ucrt\inc\corecrt_internal_strtox.h
Line: 1772
Expression: _p != nullptr
I dont have any idea what that error message means, so i dont know what im doing wrong

Also, Craig, do you mind giving an example, im pretty new to using C and am unfamiliar with fgets
Okay. Because your code was a bit far off, I had to refactor it heavily.
Your code was only handling a single customer record. What you need to do is a separate struct instance for each line (i.e. customer). So, we want to use realloc to enlarge a dynamic array of our struct customer.
And, you have allocate memory [on the heap] for each string in a record (i.e. first and last names). So, we have to use strdup
Anyway, here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct customer {
char *fName;
char *lName;
int miles;
int years;
int sequence;
};
int
main(void)
{
FILE *fp;
char buf[1000];
char *cp;
char *bp;
struct customer *list = NULL;
int custcnt = 0;
struct customer *cust;
const char *delim = "\t\n";
int field;
fp = fopen("customers.txt","r");
if (fp == NULL) {
perror("error opening file");
exit(1);
}
while (1) {
cp = fgets(buf,sizeof(buf),fp);
if (cp == NULL)
break;
// increase size of customer list/array by one
list = realloc(list,sizeof(*list) * (custcnt + 1));
if (list == NULL) {
perror("malloc");
exit(1);
}
// point to place for next customer
cust = &list[custcnt];
// advance customer count
++custcnt;
// preclear the new record in case of a line that is malformed (i.e.
// missing some data)
memset(cust,0,sizeof(*cust));
// loop through and store all customer record fields
bp = buf;
for (field = 0; field < 5; ++field) {
cp = strtok(bp,delim);
if (cp == NULL)
break;
bp = NULL;
switch (field) {
case 0:
cust->fName = strdup(cp);
break;
case 1:
cust->lName = strdup(cp);
break;
case 2:
cust->miles = atoi(cp);
break;
case 3:
cust->years = atoi(cp);
break;
case 4:
cust->sequence = atoi(cp);
break;
}
}
printf("After Reading: fName:[%s] lName:[%s] miles:[%d] years:[%d]\n",
cust->fName, cust->lName, cust->miles, cust->years);
}
fclose(fp);
return 0;
}

Related

Cannot implement Linked List in the given code

I want to insert the data in ascending order based on the partNumber.
When the function is called in main, then the node is successfully added at the first position. But on calling the function second time, there is some problem in insertion and I am unable to figure it out. When I enter the values(in second call), I get the error
Process exited after 8.277 seconds with return value 3221225477
typedef struct part {
int partNumber;
char partName[200];
int partQuantity;
struct part *nextPart;
} Part;
Part *inventory = NULL;
void insertPart();
int
main(int argc, char *argv[])
{
insertPart();
insertPart();
insertPart();
insertPart();
return 0;
}
void
insertPart()
{
Part *tempPart,
*traversePart,
*swapPart;
int counter = 0;
traversePart = inventory;
tempPart = (Part *) malloc(sizeof(Part *));
printf("Enter the Part Number\n");
scanf("%d", &(tempPart->partNumber));
getchar();
printf("Enter the Part Name\n");
fgets(tempPart->partName, 200, stdin);
printf("Enter the Part Quantity\n");
scanf("%d", &(tempPart->partQuantity));
getchar();
if (inventory == NULL) {
inventory = tempPart;
printf("Part added at the first position.\n");
}
else {
while (traversePart->nextPart->partNumber < tempPart->partNumber) {
counter++;
traversePart = traversePart->nextPart;
if (traversePart->nextPart == NULL) {
break;
}
}
if (counter == 0) {
swapPart = inventory;
inventory = tempPart;
tempPart->nextPart = swapPart;
}
else if (traversePart->nextPart == NULL) {
traversePart->nextPart = tempPart;
}
else {
swapPart = traversePart->nextPart;
traversePart->nextPart = tempPart;
tempPart->nextPart = swapPart;
}
}
printf("Element added at position : %d", counter);
}
The problem is traversePart->nextPart->partNumber traversePart->nextPart is not referring to anything or it is not holding any of the address. When you insert first value if condition is true
if (inventory == NULL) {
inventory = tempPart;
printf("Part added at the first position.\n");
}
inventory now holding the address of tempPart but while assigning values of tempPart you never assign an address to its nextvalue and it's not there because you only inserted the first value. For the second position
else{
while(traversePart->nextPart!=NULL)
{
traversePart=traversePart->nextPart;
}
if(traversePart->partNumber < tempPart->partNumber){
//here you can verify conditions
traversePart->nextPart = tempPart
}
}
You're intermixing fgets and scanf [and getchar]. Better to use just fgets and then apply strtol for numbers [or sscanf].
You're linked list code is a bit convoluted. It can be simplified.
Here's the refactored code. I've pulled some helper functions that I had lying around to do the prompting.
And, I added list printing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct part {
int partNumber;
char partName[200];
int partQuantity;
struct part *nextPart;
} Part;
Part *inventory = NULL;
void insertPart();
int getstr(char *buf,int buflen,const char *prompt);
long getnum_strtol(const char *prompt);
int
main(int argc, char **argv)
{
insertPart();
insertPart();
insertPart();
insertPart();
for (Part *cur = inventory; cur != NULL; cur = cur->nextPart)
printf("partNumber=%d partQuantity=%d partName='%s'\n",
cur->partNumber,cur->partQuantity,cur->partName);
return 0;
}
void
insertPart(void)
{
Part *tempPart;
Part *cur;
Part *prev = NULL;
int counter = 0;
#if 0
tempPart = (Part *) malloc(sizeof(Part *));
#else
tempPart = malloc(sizeof(*tempPart));
#endif
tempPart->partNumber = getnum_strtol("Enter the Part Number");
getstr(tempPart->partName,sizeof(tempPart->partName),"Enter the Part Name");
tempPart->partQuantity = getnum_strtol("Enter the Part Quantity");
tempPart->nextPart = NULL;
// find the tail/end of the list
for (cur = inventory; cur != NULL; cur = cur->nextPart) {
++counter;
// insert in sorted part order
if (cur->partNumber > tempPart->partNumber)
break;
prev = cur;
}
do {
tempPart->nextPart = cur;
// insert in the middle or end of list
if (prev != NULL) {
prev->nextPart = tempPart;
break;
}
// insert in new list or before first element of existing list
tempPart->nextPart = inventory;
inventory = tempPart;
} while (0);
printf("\nElement added at position : %d\n", counter);
}
// getstr -- get a string with prompt
// RETURNS: length or (<0 -> error)
int
getstr(char *buf,int buflen,const char *prompt)
{
char *cp;
int ret = 0;
// NOTE: usage of the error codes in errno.h is arbitrary
while (ret <= 0) {
// ensure buffer has enough space
if (buflen < 2) {
ret = -ENOMEM;
break;
}
// output prompt
if (prompt != NULL) {
printf("%s: ",prompt);
fflush(stdout);
}
// get a line
cp = fgets(buf,buflen,stdin);
// EOF
if (cp == NULL) {
ret = -ENODATA;
break;
}
// get buffer length
ret = strlen(buf);
// empty string
if (ret <= 0)
continue;
// point to last char
cp = &buf[ret - 1];
// ensure we got a newline -- if not, fgets had to chop the line (i.e.)
// the line is too long to fit in the buffer
if (*cp != '\n') {
ret = -ENOSPC;
break;
}
// strip the newline -- we are done
*cp = 0;
--ret;
}
return ret;
}
// getnum_strtol -- get number using strtol
long
getnum_strtol(const char *prompt)
{
int len;
int readflg = 1;
char *cp;
char buf[100];
long num = 0;
while (readflg) {
len = getstr(buf,sizeof(buf),prompt);
if (len < 0)
exit(1);
num = strtol(buf,&cp,10);
// ensure we got a least one digit
if (cp <= buf)
continue;
switch (*cp) {
case ' ':
case '\t':
case 0:
readflg = 0;
break;
default:
printf("getnum_strtol: not a valid number -- buffer '%s', invalid '%s'\n",
buf,cp);
break;
}
}
return num;
}
Here's the input file I used to test:
37
Hex Bolt
12
28
Machine Screw
6
23
Brad Nail
1000
27
Lock Nut
300
Here's the program output:
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 0
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 1
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 1
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 2
partNumber=23 partQuantity=1000 partName='Brad Nail'
partNumber=27 partQuantity=300 partName='Lock Nut'
partNumber=28 partQuantity=6 partName='Machine Screw'
partNumber=37 partQuantity=12 partName='Hex Bolt'

Reading a file containing student names and ages and displaying them in sorted order

When I run this program, it only prints names upto 'I' and not all the way to 'Z'. I've tried to first read the file and store it's contents into a linked list and then display the contents in sorted order. Below there is the file from which the program is reading and the program itself. Please help.
The File:
Samir 20
Arup 18
Neha 22
Ashim 19
Issac 21
The Program:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
struct student
{
char name[20];
int age;
struct student *pre, *next;
};
struct student *s, *f;
s = (struct student *)malloc(sizeof(struct student));
s->pre = NULL;
s->next = NULL;
f = s;
fp = fopen("A.txt", "r");
if(fp == NULL)
{
printf("Not Opened");
exit(0);
}
while(1)
{
if(fscanf(fp, "%s %d", s->name, &s->age) == EOF)
{
s = s->pre;
s->next = NULL;
break;
}
else
{
s->next = (struct student *)malloc(sizeof(struct student));
s->next->pre = s;
s->next->next = NULL;
s = s->next;
}
}
s = f;
char ch = 'A';
while(1)
{
if(ch == 'Z'+1)
break;
while(1)
{
if(f->name[0] == ch)
{
printf("%s %d\n", f->name, f->age);
f->next->pre = f->pre;
f->pre->next = f->next;
if(f->next == NULL)
break;
else
f = f->next;
}
if(f->next == NULL)
break;
else
f = f->next;
}
ch = ch +1;
f = s;
}
fclose(fp);
}
The issue is with the following lines:
f->next->pre = f->pre;
f->pre->next = f->next;
If you remove these then the list is printed just fine. However, only the printing is ordered not the list. If you'd like to order the list then see:
Sort a Linked list using C
Seems you mix two concepts: sorting and printing.
if(f->name[0] == ch) then you print it and you re-link the list. I don't know why you re-link it and I didn't check if you did it right to sort it (I feel it is not).
Either first sort the list (e.g. implement a bubble or use quick sort) and then print it, or just print the list as you do now but remove the re-linking (then it would print fine - except that AB could be printed before AA because you only check the first letter).

How to edit .csv files in C

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);

Importing a CSV list to an array structure in C

So i'm working on a class project that is going to manage a stores inventory. The program will need to remove/add products as well as update stock counts. When the program runs the structure array needs to be initialized by a inventory file that its values are separated by commas which saves all the data after each use.
Here is the file data.
1000,1.49,3.79,10,0,Fish Food
2000,0.29,1.59,100,1,Angelfish
2001,0.09,0.79,200,1,Guppy
5000,2.40,5.95,10,0,Dog Collar (Large)
6000,49.99,129.99,3,1,Dalmatian Puppy
Here is the structure layout.
struct inventory_s
{
int productNumber;
float mfrPrice;
float retailPrice;
int numInStock;
char liveInv;
char productName[PRODUCTNAME_SZ];
};
And here is the code
int fileData()
{
FILE* pFile;
char *buf = malloc(MAX_INVENTORY); // MAX INVENTORY = 50
char *info;
if ( ( pFile = fopen( "inventory.txt", "r" ) ) == NULL ) //Reading a file
{
printf( "File could not be opened.\n" );
}
int i = 0;
while (fgets(buf, MAX_INVENTORY, pFile) != NULL)
{
if ((strlen(buf)>0) && (buf[strlen (buf) - 1] == '\n'))
buf[strlen (buf) - 1] = '\0';
info = strtok(buf, ",");
inventory[i].productNumber = atoi(info);
info = strtok(buf, ",");
inventory[i].mfrPrice = atof(info);
info = strtok(NULL, ",");
inventory[i].retailPrice = atof(info);
info = strtok(NULL, ",");
inventory[i].numInStock = atoi(info);
info = strtok(NULL, ",");
strcpy(inventory[i].liveInv, info);
info = strtok(NULL, ",");
strcpy(inventory[i].productName, info);
printf("%i, %f, %f, %i, %s, %s \n", inventory[i].productNumber , inventory[i].mfrPrice, inventory[i].retailPrice , inventory[i].numInStock, inventory[i].liveInv, inventory[i].productName );
i++;
}
fclose(pFile);
return 0;
}
Right now I dont get anything if I run the code, but if I run only up to the first initialization
info = strtok(buf, ",");
inventory[i].productNumber = atoi(info);
I get all the first values assigned correctly.
Continuing from the comment, It is hard to tell without seeing your entire code where the issue may be. However, the thing that jumps out is there is no need to strip the trailing '\n' from buf before calling strtok. You are better served by using a delimiter of ",\n" and letting strtok handle the final string in each line.
A short example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { PRODUCTNAME_SZ = 32, MAXINV = 128, MAXC = 512 };
typedef struct {
int productNumber;
float mfrPrice;
float retailPrice;
int numInStock;
char liveInv;
char productName[PRODUCTNAME_SZ];
} inventory_s;
int main (int argc, char **argv) {
inventory_s *inv = NULL;
char buf[MAXC] = "", *delim = ",\n";
size_t allocsz = MAXINV, idx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
if (!(inv = malloc (sizeof *inv * allocsz))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
char *p = buf;
size_t val = 0; /* tokenize line with strtok */
for (p = strtok (p, delim); p; p = strtok (NULL, delim))
switch (val) { /* switch controls assignment */
case 0 : inv[idx].productNumber = (int)strtol (p, NULL, 10);
val++; break;
case 1 : inv[idx].mfrPrice = strtof (p, NULL);
val++; break;
case 2 : inv[idx].retailPrice = strtof (p, NULL);
val++; break;
case 3 : inv[idx].numInStock = (int)strtol (p, NULL, 10);
val++; break;
case 4 : inv[idx].liveInv = (char)strtol (p, NULL, 10);
val++; break;
case 5 : strncpy (inv[idx].productName, p, PRODUCTNAME_SZ);
inv[idx].productName[PRODUCTNAME_SZ-1] = 0; break;
}
if (val != 5) { /* validate all struct members filled */
fprintf (stderr, "error: incomplete parse of values line: %zu\n",
idx+1);
return 1;
}
if (++idx == allocsz) { /* realloc when allocsz reached */
void *tmp = realloc (inv, (allocsz + MAXINV) * sizeof *inv);
if (!tmp) {
fprintf (stderr, "error: realloc - virtual memory exhausted.\n");
break; /* leave read loop preserving exhisting data in inv */
}
inv = tmp;
allocsz += MAXINV;
}
}
for (size_t i = 0; i < idx; i++)
printf ("%5d %6.2f %7.2f %4d %d %s\n", inv[i].productNumber,
inv[i].mfrPrice, inv[i].retailPrice, inv[i].numInStock,
inv[i].liveInv, inv[i].productName);
free (inv); /* free allocated memory */
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
note: if your liveInv value is really just a single digit, then you can replace (char)strtol (p, NULL, 10); with simply *p - '0';.
(You may need to set PRODUCTNAME_SZ larger depending on your full dataset)
Example Use/Output
$ ./bin/inventory <dat/inventory.txt
1000 1.49 3.79 10 0 Fish Food
2000 0.29 1.59 100 1 Angelfish
2001 0.09 0.79 200 1 Guppy
5000 2.40 5.95 10 0 Dog Collar (Large)
Look it over and let me know if you have any questions.
I'm a Java Programmer but Now Building Its so called Java Collections in C &C++. This code is a Part of My Project "Big Data Analysis Using C & C++".
I already had the code, so, modified for your data. It can take any no. of columns. Just pass opened file pointer & separation type.
You can get a particular column just by removing all the if part of j (ex. if(j==1), etc) to your particular column. Most people forget this. :-D
#include <iostream>
#include <stdio.h>
int MAX=1000; // Size of Single String
void analyse(FILE *fp, char separation) {
/* fp is file pointer (already opened)
* separation is how data is separated (' ' or '\t' or ',')
*/
char str[MAX],ch;
int i=-1;
long j=0;
// J is Column. This Can Be Applied For Any No. of Columns.
cout<<"Reading File.."<<endl;
while((ch=getc(fp))!=EOF) {
i++;
if(ch!=separation && ch!='\n')
str[i] = ch;
else {
str[i] = '\0';
if(ch == separation || ch == '\n') {
j++;
}
// At Each if of j, you can write you code to store them.
if(j == 1) {
cout<<"Product No. : "<<str<<endl;
}
if(j == 2) {
cout<<"MFR Price : "<<str<<endl;
}
if(j == 3) {
cout<<"Retail : "<<str<<endl;
}
if(j == 4) {
cout<<"Num in Stock : "<<str<<endl;
}
if(j == 5) {
cout<<"LiveInv : "<<str<<endl;
}
if(j == 6) {
cout<<"Product Name : "<<str<<endl;
}
cout<<"-------------------------------------"<<endl;
if(ch == '\n')
j = 0;
i = -1;
}
}
cout<<"Reading Completed.."<<endl;
}

Read tab delimited file to Structure in C

I have a file with tab delimited data. I want to read the every line into a Structure. I have a code to read the data to char buffer. But I want to load the data into a Structure.
This is My sample data.
empname1\t001\t35\tcity1
empname2\t002\t35\tcity2
My Structure definition .
struct employee
{
char *empname;
char *empid;
int age;
char *addr;
};
My sample program to read data to a char array buffer
char buffer[BUF_SIZE]; /* Character buffer */
input_fd = open (fSource, O_RDONLY);
if (input_fd == -1) {
perror ("open");
return 2;
}
while((ret_in = read (input_fd, &buffer, BUF_SIZE)) > 0){
// Do Some Process
}
Here I want to load the content to a structure variable instead of the character buffer. How I can achieve that?
Well, a possible solution could be
Read a complete line from the file using fgets().
tokenize the input buffer based on the required delimiter [tab in your case] using strtok().
allocate memory (malloc()/ realloc()) to a pointer variable of your structure.
copy the tokenized inputs into the member variables.
Note:
1. fgets() reads and stores the trailing \n.
2. Please check carefully how to use strtok(). The input string should be mutable.
3. Allocate memory to pointers before using them. IMO, use statically allocated array as struct employee member variables.
You can use the fscanf function. Open a file as a stream then use the fscanf to get a input from the file.
int fscanf(FILE *stream, const char *format, ...);
FILE *fp=fopen(fsource,"r+");
struct employee detail;
fscanf(fp,"%s %s %d %s",detail.empname,detail.empid,&detail.age,detail.addr);
Make sure that allocation of memory to the variables.
Or else you can use the strtok function. That time you have to use the sscanf function.
You can use fscanf to read each line from file, strtok to tokenize the line read.
Since your structure members are pointers, allocate memory appropriately.
The following minimal code does exactly what you want.
#define SIZE 50
FILE *fp = NULL;
int i = 0;
struct employee var = {NULL, NULL, 0, NULL};
char line[SIZE] = {0}, *ptr = NULL;
/* 1. Open file for Reading */
if (NULL == (fp = fopen("file.txt","r")))
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
/* 2. Allocate Memory */
var.empname = malloc(SIZE);
var.empid = malloc(SIZE);
var.addr = malloc(SIZE);
/* 3. Read each line from the file */
while (EOF != fscanf(fp, "%s", line))
{
/* 4. Tokenise the read line, using "\" delimiter*/
ptr = strtok(line, "\\");
var.empname = ptr;
while (NULL != (ptr = strtok(NULL, "\\")))
{
i++;
/* 5. Store the tokens as per structure members , where (i==0) is first member and so on.. */
if(i == 1)
var.empid = ptr;
else if(i == 2)
var.age = atoi(ptr);
else if (i == 3)
var.addr = ptr;
}
i = 0; /* Reset value of i */
printf("After Reading: Name:[%s] Id:[%s] Age:[%d] Addr:[%s]\n", var.empname, var.empid, var.age, var.addr);
}
Working Demo: http://ideone.com/Kp9mzN
Few things to Note here:
This is guaranteed to work, as long as your structure definition (and order of members) remains the same (see manipulation of value i).
strtok(line, "\\");, Second argument is just escaping (first \) the actual \ character.
Clarification from the OP:
In your structure definition, third member is an int, however you're trying to read t35 into it (which is a string).
So var.age = atoi(ptr); will give you 0,
You could change the structure definition, making third member as char * and allocating memory like other members.
Or change file contents, making sure an int is present as the third value.
I think this may be what you are looking for
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
struct employee
{
char *empname;
char *empid;
int age;
char *addr;
};
int readEmploee(char *line, struct employee *employee)
{
char *token;
char *saveptr;
char *endptr;
if ((employee == NULL) || (line == NULL))
return 0;
token = strtok_r(line, "\t", &saveptr);
if (token == NULL)
return 0;
employee->empname = strdup(token);
token = strtok_r(NULL, "\t", &saveptr);
if (token == NULL)
return 0;
employee->empid = strdup(token);
token = strtok_r(NULL, "\t", &saveptr);
if (token == NULL)
return 0;
employee->age = strtol(token, &endptr, 10);
if (*endptr != '\0')
return 0;
token = strtok_r(NULL, "\t", &saveptr);
if (token == NULL)
return 0;
employee->addr = strdup(token);
return 1;
}
char *mygetline(int fd)
{
char *line;
size_t length;
size_t count;
char character;
line = malloc(128);
if (line == NULL)
return NULL;
length = 0;
count = 1;
do
{
if (read(fd, &character, 1) != 1) /* end of file probably reached */
{
free(line);
return NULL;
}
else if (character != '\n')
{
if (length > 128 * count)
{
char *temp;
temp = realloc(line, 128 * count);
if (temp == NULL)
{
free(line);
return NULL;
}
line = temp;
count += 1;
}
line[length++] = character;
}
} while (character != '\n');
line[length] = 0;
return line;
}
struct employee *readFile(const char *const fSource, size_t *count)
{
struct employee *employees;
int employeeCount;
int input_fd;
char *line;
if ((count == NULL) || (fSource == NULL))
return NULL;
*count = 0;
employees = NULL;
employeeCount = 0;
input_fd = open (fSource, O_RDONLY);
if (input_fd == -1)
{
perror ("open");
return NULL;
}
while ((line = mygetline(input_fd)) != NULL)
{
struct employee employee;
if (readEmploee(line, &employee) != 0)
{
struct employee *temp;
temp = realloc(employees, (1 + employeeCount) * sizeof(struct employee));
if (temp != NULL)
employees = temp;
employees[employeeCount++] = employee;
}
free(line);
}
*count = employeeCount;
return employees;
}
int
main()
{
size_t count;
size_t index;
struct employee *employees;
employees = readFile("somesamplefile.txt", &count);
if (employees == NULL)
return 1;
for (index = 0 ; index < count ; index++)
{
struct employee current;
current = employees[index];
fprintf(stderr, "%s, %s, %d, %s\n", current.empname, current.empid, current.age, current.addr);
if (current.empname != NULL)
free(current.empname);
if (current.empid != NULL)
free(current.empid);
if (current.addr != NULL)
free(current.addr);
}
free(employees);
return 0;
}

Resources