fscanf() filter - c

I have a file with data in this format:
Name WeekDay Month day, Year StartHour:StartMin Distance Hour:Min:Sec
Example:
John Mon September 5, 2011 09:18 5830 0:26:37
I want to scan this into a struct:
typedef struct {
char name[20];
char week_day[3];
char month[10];
int day;
int year;
int startHour;
int startMin;
int distance;
int hour;
int min;
int sec;
} List;
i use fscanf():
List listarray[100];
for(int i = 0; ch = fgetc(file) != 'EOF'; ch = fgetc(file), i++){
if(ch != '\0'){
fscanf(file, "%s %s %s %d %d %d %d %d %d %d %d", &listarray[i].name...etc)
}
}
My issue is that I want to filter out the noise in the input string, that being:
Month day*,* year <- the comma is consistent in all entries. I just want the month in the char array, the day in int.
And the time stamps:
startHour:startmin and hour:min:sec <- here I want to filter out the colon.
Do I need to put it into a string first and then do some splitting, or can I handle it in fscanf?
Update:
Okay, så I've been trying to get this to work now, but I simply cannot. I literally have no idea what the issue is.
#include <stdio.h>
/*
Struct to hold data for each runners entry
*/
typedef struct {
char name[21];
char week_day[4];
char month[11];
int date,
year,
start_hour,
start_min,
distance,
end_hour,
end_min,
end_sec;
} runnerData;
int main (int argc, const char * argv[])
{
FILE *dataFile = fopen("/Users/dennisnielsen/Documents/Development/C/Afleveringer/Eksamen/Eksamen/runs.txt", "r");
char ch;
int i, lines = 0;
//Load file
if(!dataFile)
printf("\nError: Could not open file!");
//Load data into struct.
ch = getc(dataFile);
//Find the total ammount of lines
//To find size of struct array
while(ch != EOF){
if(ch == '\n')
lines++;
ch = getc(dataFile);
}
//Allocate memory
runnerData *list = malloc(sizeof(runnerData) * lines);
//Load data into struct
for(i = 0; i < lines; i++){
fscanf(dataFile, "%s %s %s %d, %d %d:%d %d %d:%d:%d %[\n]",
list[i].name,
list[i].week_day,
list[i].month,
list[i].date,
list[i].year,
list[i].start_hour,
list[i].start_min,
list[i].distance,
list[i].end_hour,
list[i].end_min,
list[i].end_sec);
printf("\n#%d:%s", i, list[i].name);
}
fclose(dataFile);
return 0;
}
I've been told that "only strings to do not require & in front of them in fscanf();" but I tried both with and without ampersand to no avail.

Put the "noise" in the format string.
Also you might like to limit the size of strings.
And get rid of the & for arrays.
And test the return value from scanf!
// John Mon September 5, 2011 09:18 5830 0:26:37
if (scanf("%19s%2s%9s%d,%d%d:%d%d%d:%d:%d", ...) != 11) /* error */;
// ^^^ error: not enough space
Notice week_day has space for 2 characters and the zero terminator only.

You can put this noise in the scanf format string.
Note also that for date/time strings, you can use strptime. It does the same kind of job that scanf, but is specialized on date/times. You will bo able to use %Y, %M ... and other with it.

Related

Question about reading int and String from array

I have two-dimensional arrays which contain the input from a file. I want to assign integers and strings from the array to different variables; the integer is set correctly, but the string is not working.
the input is like:
(1,2) apple 2 3 north
but all these information are inside:
char data[MAX_LINES][MAX_LEN];
I am trying to use sscanf to assign values:
sscanf(data[i],"(%d,%d) %8s %d %d %8s",&x,&y,type,&age,&hun,direction);
Code structure by ignoring unrelated code
FILE *in_file = fopen(fileName,"r");
char data[MAX_LINES][MAX_LEN];
int x,y,age,hun;
char type[10];
char deriction[20];
if(! in_file){
printf("cannot read file\n");
exit(1);
}
int line=0;
while(!feof(in_file) && !ferror(in_file)){
if(fgets(data[line],MAX_LEN,in_file) !=NULL ){
char *check = strtok(data[line],d);
line++;
}
}
fclose(in_file);
for(int i = 9; i<14;i++){
sscanf(data[i],"(%d,%d) %8s %d %d %8s",&x,&y,type,&age,&hun,deriction);
}
#include <stdio.h>
int main(){
//Data:
char data[1][100] = {"(1,2) apple 2 3 north"};
//Define variables:
int x, y, age, hun;
char type[10], direction[10];
sscanf(data[0], "(%d,%d) %s %d %d %s", &x, &y, type, &age, &hun, direction);
//Check by printing it out:
printf("(%d,%d) %s %d %d %s\n", x, y, type, age, hun, direction);
printf("x: %d\ny: %d\ntype: %s\nage: %d\nhun: %d\ndirection: %s", x, y, type, age, hun, direction);
//Success:
return 0;
}
If this doesn't work, then the problem might be the data in your array, data.
You have the line char *check = strtok(data[line],d);. You've not shown what d is, but unless it is a string with no characters in common with the input line, strtok() just mangled your stored data, zapping whatever is the first character from d with a null byte. That means your sscanf() will fail because it isn't looking at the whole line. If, perchance, you have const char d[] = "\r\n"; or equivalent, this ceases to be relevant, beyond pointing out that you should show enough code to reproduce the problem.

C Array struct, trying to access data, but is coming up with the same for all arrays

So, my goal is to create a linear search, but i have got that down pat, I am having one problem with accessing strings from the struct, that i have stored using a txt file, so in linearSearch() I tried doing this:
printf("Name: %s \n", q.name[i]);
printf("Data: %d \n", q.data[i]);
The data would be perfect but name would just print out the same name for every array which would be the last item that I put into the array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char* name[10];
int data[10];
}Word;
//int bubblesort (Word word);
void linearSearch(char* name, Word q);
int main (int argc, const char *argv[]){
Word q;
char username[9]; /* One extra for nul char. */
int score;
int i = 0;
FILE *ifp, *ofp;
ifp = fopen("Data.txt", "r");
while (fscanf(ifp, "%s %d", &username, &score) == 2) {
q.name[i] = username;
printf ("Name: %s, I = %d \n", q.name[i], i);
q.data[i] = score;
printf ("Data: %d, I = %d \n", q.data[i], i);
i++;
}
linearSearch("Matt", q);
return EXIT_SUCCESS;
}
void linearSearch(char* name, Word q){
int i = 0;
int foundIt = 0;
int numNames = sizeof(&q.name);
while ((foundIt == 0) && (i <= numNames)){
printf("Name: %s \n", q.name[i]);
printf("Data: %d \n", q.data[i]);
if ((strcmp(name, q.name[i]) != 0)){
i = i + 1;
} else {
foundIt = 1;
}
}
if (foundIt == 1){
printf("Name found at position %d", i);
} else {
printf("Required person not found");
}
}
This happens because of the code
q.name[i] = username;
You cannot assign the value of an array using = operator. Here, you're assigning the address of username to every q.name[i]. So, the last value of username is reflected throughout the array.
What you actually need is to use malloc() to allocate memory and then strcpy() to copy the string contents.
Otherwise, you can also make use of strdup().
Either way, don't forget to free() the allocated ememory once you're done using them.
I can see that you declared char username[9], so I assume your names should be at most 8 characters long. You should :
read with : fscanf(ifp, "%8s %d",&username, &score) == 2 : the & is useless in front of an array (it decays nicely to a pointer), but you should limit size of input - ok , your problem does not come from there
use a 2D char array for Word.name instead of an array of pointers. That way your memory is already allocated and you can safely strcpy to it :
typedef struct {
char name[10][9];
int data[10];
}Word;
then :
strcpy(q.name[i], username); /* safe because qname[i] and username are both [9] */
The rule here is always control that you do not risk a buffer overrun when writing in char arrays/
An alternative way would be to do dynamic allocation through strdup but in that case you should free it.

fscanf to structure array

I'm trying to take some input from a text file, put it into a structure and print it out. The sample text file looks like this:
2
Curtis
660-------
Obama
2024561111
(Digits on the first number dashed out (for privacy), second is the Whitehouse.gov one, I called, they can't help me.)
Sample output:
204-456-1111 Obama
660--------- Curtis
(Formatting and sorting shouldn't be a problem when I figure out the rest.)
My question is labeled by the question marks below (in the first FOR loop, how do I get specific lines out of the text file to create the structures?
#include <stdio.h>
#include <string.h>
struct telephone {
char name[80];
long long int number;
}
main() {
struct telephone a, b;
char text[80];
int amount, i;
FILE *fp;
fp = fopen("phone.txt", "r");
fscanf(fp, "%d", amount);
struct telephone list[amount];
for(i = 0; i < amount; i++) {
strcpy(list[i].name, ???);
list[i].number, ???);
}
fclose(fp);
for(i = 0; i < amount; i++) {
DisplayStruct(list[i]);
}
}
DisplayStruct(struct telephone input) {
printf("%lld %s\n", input.number, input.name);
}
Use fgets to read one line at a time.
int lnum = 0;
char line[100];
while( fgets(line, sizeof(line), fp) ) {
lnum++;
printf( "Line %d : %s\n", lnum, line );
}
You can then use sscanf or strtok or numerous other approaches to pull data out of the string you just read.
I advise against storing your phone number as an integer. Phone numbers are better represented as strings.
If you can guarantee that neither names nor phone numbers have blanks in them, you can utilize fscanf() to read this data:
for(i = 0; i < amount; i++) {
fscanf("%s %lld", list[i].name, &list[i].phone);
}
Things to keep in mind:
You must check for conversion errors
This approach is less tolerant to input errors (in case of using fgets() it might be easier to recover and drop the malformed entry - unless the record has wrong number of fields).
Agree with #paddy, use a string to store phone numbers. (Cope with leading 0s, variant length, #, *, pause, etc.). Might as well also make sure it is big enough for a int64_t.
Note: The web has examples of 22 digits.
struct telephone {
char name[80];
char number[21];
}
To read in the data ...
for (i = 0; i < amount; i++) {
// +1 for buffer size as string read has a \n which is not stored.
char na[sizeof list[0].name + 1];
char nu[sizeof list[0].number + 1];
if ((fgets(na, sizeof na, fp) == NULL) || (fgets(nu, sizeof nu, fp) == NULL)) {
break; // TBD, Handle unexpected missing data
}
// The leading space in the format will skip leading white-spaces.
if (1 != sscanf(na, " %79[^\n]", list[i].name)) {
break; // TBD, Handle empty string
}
if (1 != sscanf(na, " %20[^\n]", list[i].number)) {
break; // TBD, Handle empty string
}
}
if (fgetc(fp) != EOF) {
; // Handle unexpected extra data
}
amount = i;
To write
// Pass address of structure
for(i = 0; i < amount; i++) {
DisplayStruct(&list[i]);
}
void DisplayStruct(const struct telephone *input) {
if (strlen(input->number) == 10) {
printf("%.3s-%.3s-%4s", input->number, &input->number[3], &input->number[6]);
}
else { // tbd format for unexpected telephone number length
printf("%13s", input->number);
}
// Suggest something around %s like \"%s\" to encapsulate names with spaces
printf(" %s\n", input->name);
}

C - using fgetc to store value in enum 'class'

I am writing a program that accepts a file with database entries in it. The entries are all in the same format, with the data in the same order. The first number in the file is the number of entries. Then the data looks like this:
LastName FirstName StudentID age year GPA expectedGraduationDate
Ex:
Doe John 12345678 23 freshman 4.0 2013
My issue is with the year value. We are supposed to declare it as type 'class', which is supposed to be enum class{freshman, sophomore, junior, senior, grad};
I have a header file with the following declaration:
typedef enum {firstYear, sophomore, junior, senior, grad} class;
Then my main file:
#include <stdio.h>
#include <stdlib.h>
#include "class.h"
int main(int argc, char *argv[]){
typedef struct{
int DBrecordID; //ID for each entry, range 0-319
char *last; //student last name
char *first; //student first name
char studentID[8]; //student ID
int age; //student age
class year; //year in school
float gpa; //GPA
int expGradYear; //expected graduation year
}DBrecord;
int numEntries;
DBrecord **record;
char buffer[80];
FILE *fpt;
int c, i;
int j = 0;
//check for invalid file arguments
if(argc != 2){
printf("Number of arguments is invalid\n");
exit(1);
}
//error if unable to open file
if((fpt = fopen(argv[1], "r")) == NULL){
printf("Error: Couldn't open file.\n");
exit(1);
}
//set file pointer to read passed file
fpt = fopen(argv[1], "r");
//scan first int in file and assign to numEntries
//fscanf(fpt, "%d", &numEntries);
//allocate memory for structures, each is 36 bytes
*record = malloc (sizeof (DBrecord) * numEntries);
//loop through each DB entry
do{
for(i=0; i<numEntries; i++){
numEntries = record[i]->DBrecordID;
do{
c=fgetc(fpt);
buffer[j++] = c;
}while(c != ' ');
strcpy(record[i]->last, buffer);
j=0;
do{
c=fgetc(fpt);
buffer[j++] = c;
}while(c != ' ');
strcpy(record[i]->first, buffer);
j=0;
do{
c=fgetc(fpt);
buffer[j++] = c;
}while(c != ' ');
strcpy(record[i]->studentID, buffer);
j=0;
do{
c=fgetc(fpt);
memcpy(c, buffer[j++], 1);
}while(c != ' ');
memcpy(buffer, record[i]->year, 4);
j=0;
do{
c=fgetc(fpt);
buffer[j++] = c;
}while(c != ' ');
memcpy(buffer, record[i]->gpa, 4);
j=0;
do{
c=fgetc(fpt);
buffer[j++] = c;
}while(c != ' ' || c != '\n');
memcpy(buffer, record[i]->expGradYear, 4);
j=0;
}
}while(c != EOF);
return 0;
}
*Updated errors
main.c:75: warning: passing arg 1 of `memcpy' makes pointer from integer without a cast
main.c:75: warning: passing arg 2 of `memcpy' makes pointer from integer without a cast
main.c:77: incompatible type for argument 2 of `memcpy'
main.c:83: incompatible type for argument 2 of `memcpy'
main.c:89: warning: passing arg 2 of `memcpy' makes pointer from integer without a cast
main.c:94: parse error before "DBrecord"
So I'm assuming I can't do what I'm trying to do with memcpy, or I'm just doing it wrong. Suggestions?
There quite a few errors in the program but for a start
1) record should be of type DBrecord* not DBrecord**
2) strcpy takes destination as the first argument so this wouldn't work
strcpy(buffer, record[i]->last);
3) you also need to allocate memory for record[i]->last
4) strcpy is used to copy strings so if you wan't to store to float or int i.e. gpa etc you need to use memcpy also the value from buffer should be converted using strol strod
also would recommend to get hold of this book K&R it would be really helpful overtime
Why would you try to strcpy (String Copy) from an int, like expGradYear?
strcpy is for STRINGS. If you want to append an int to a buffer, use memcpy (MEMory Copy)

Reading in from a file in C

I need to read from a file from using C.
#include <stdio.h>
struct record{
char name[2];
int arrival_time;
int job_length;
int job_priority;
};
const int MAX = 40;
main(){
struct record jobs[MAX];
FILE *f;
fopen("data.dat","rb");
int count =0;
while(fscanf(f, "%c%c %d %d %d", &jobs[count].name, &jobs[count].arrival_time, &jobs[count].job_length, &jobs[count].job_priority) != EOF){
count++;
}
int i;
for(i =0;i<count;i++){
printf("%c%c %d %d %d", &jobs[count].name, &jobs[count].arrival_time, &jobs[count].job_length, &jobs[count].job_priority);
}
}
The data file's format is the following:
A1 3 3 3
B1 4 4 4
C1 5 5 5
...
First one is char[2] and the other three int. I can't get the code right to read in until the end of file.
Anyone come across this before?
Updated Code.
There are a couple of modifications needed - most notably, you need to reference the jobs array of structures:
#include <stdio.h>
struct record{
char name[2];
int arrival_time;
int job_length;
int job_priority;
};
const int MAX = 40;
int main(void)
{
struct record jobs[MAX];
int i = 0;
int j;
FILE *f = fopen("data.dat","rb");
while (fscanf(f, "%c %d %d %d", &jobs[i].name[0], &jobs[i].arrival_time,
&jobs[i].job_length, &jobs[i].job_priority) == 4 && i < MAX)
i++:
for (j = 0; j < i; j++)
printf("%c %d %d %d\n", jobs[j].name[0], jobs[j].arrival_time,
jobs[j].job_length, jobs[j].job_priority);
return(0);
}
I make sure the loop doesn't overflow the array. I print the data out (it is the most basic form of checking that you've read what you expected). The most subtle issue is the use of jobs[i].name[0]; this is necessary to read and print a single character. There's no guarantee that there is any particular value in jobs[i].name[1]; in particular, it is quite probably not an NUL '\0', and so the name is not null terminated. It seems a bit odd using a single character name; you might want to have a longer string in the structure:
char name[MAX]; // Lazy reuse of MAX for two different purposes!
fscanf(f, "%.39s ...", jobs[i].name, ...
printf("%s ...", jobs[i].name, ...
The & is now not needed. The %.39s notation is is used to read a length-limited string up to the first space. Note that the size in the string is one less than the size of the array. It can often be simplest simply to create the format string with sprintf() to get the size right.
The code does not error check the fopen() statement.
Your code prints out A 1 3 3 instead of A1 3 3 3. I tried to add a second %c to it and it did not resolve it.
I discussed names longer than one character...
If you need to read "A1", you need the name member to be bigger so that you can null terminate the string, and you need to use %s rather than %c to read the value, and you need to lose the & and subscript, and you need to protect your code from buffer overflow (because where you expect 'A1', someone will inevitably enter 'A1B2C3D4D5F6G7H8I9J10K11L12M13' and wreak havoc on your code if you do not protect against the buffer overflow. Incidentally, the change in the test of the result of fscanf() (from == EOF to != 4 was also a protection against malformed input; you can get zero successful conversions without reading any characters, in general - though your %c would eat one character per iteration).
#include <stdio.h>
struct record{
char name[4];
int arrival_time;
int job_length;
int job_priority;
};
const int MAX = 40;
int main(void)
{
struct record jobs[MAX];
int i = 0;
int j;
FILE *f = fopen("data.dat","rb");
while (fscanf(f, "%.3s %d %d %d", jobs[i].name, &jobs[i].arrival_time,
&jobs[i].job_length, &jobs[i].job_priority) == 4 && i < MAX)
i++:
for (j = 0; j < i; j++)
printf("%s %d %d %d\n", jobs[j].name, jobs[j].arrival_time,
jobs[j].job_length, jobs[j].job_priority);
return(0);
}
You need to get the line and parse it according to the type you want to read.
while(fgets(line, 80, fr) != NULL)
{
/* get a line, up to 80 chars from fr. done if NULL */
sscanf (line, "%s %d %d %d", &myText, &int1, &int2, &int3);
}
See example here:
http://www.phanderson.com/files/file_read.html
Note: this is not the best way, someone else has answered with a much easier solution using format strings and fgets which will basically do what i wanted to do in one easy line.
I am assuming you want something like this. Note that I have not tested this at all as I just wrote it out pretty quickly.
#include <stdio.h>
FILE *fileInput;
main()
{
char* path="whatever.txt";
fileInput = fopen(path, "r");
int i=0;
while (fgets(line,100,fi)!=NULL)
{
token[0] = strtok(line, " ");
//now you can do whatever you wanna do with token[0]
//in your example, token[0] is A1 on the first iteration, then B1, then C1
while(token[i]!= NULL)
{
//now token[i] can be converted into an int, probably using atoi
//in your example, token[i] will reference 3 (3 times) then 4, etc
}
}
}
Hope it helps!
No, the lines do not contain ints. Each line is a string which can be easily parsed into four shorter strings. Then each of the strings can either be extracted and stored in your struct, or converted to ints which you can store in your struct.
The scanf function is the usual way that people do this kind of input string parsing when the data format is simple and straightforward.

Resources