I have a data file with strings, floats, and integers separated by a single comma and a random number of spaces.
for example:
john , smith , 3.87 , 2, 6
I would like to scan each value into a struct containing str,str,float,int,int & ignore the commas and spaces. I have figured out up to the float but cant seem to get the intgers. any help would be appreciated my code is as follows:
typedef struct applicant {
char first[15];
char last[15];
float gpa;
int grev;
int greq;
} num1;
int main(int argc, char *argv[])
{
FILE *in, *out;
in = fopen(argv[1], "r");
out = fopen(argv[2], "w");
num1 class[10];
int i;
fscanf(in, "%[^,],%[^,],%f, %d, %d\n", class[i].first, class[i].last, &class[i].gpa, &class[i].grev, &class[i].greq);
fprintf(out, "%s %s %f %d %d", class[i].first, class[i].last, class[i].gpa, class[i].grev, class[i].greq);
As sturcotte06 mentioned you should use strtok() function alongside with atoi() and atof() to get the expected result.
char text[] = "john , smith , 3.87 , 2, 6";
strcpy(class[i].first, strtok(text, ","));
strcpy(class[i].last, strtok(NULL, ",");
class[i].gpa = atof(strtok(NULL, ","));
class[i].grev = atoi(strtok(NULL, ","));
class[i].greq) = atoi(strtok(NULL, ","));
I suggest the following approach.
Read the contents of the file line by line.
I am assuming the white spaces are not relevant. If that is indeed the case, replace the comma characters with spaces.
Use a simpler format to read the data from the line of text to your struct.
Always check the return value of functions that read data from an input stream to make sure that you use the data only if the read operation was successful.
// Make it big enough for your needs.
#define LINE_SIZE 200
char line[LINE_SIZE];
if ( fgets(line, LINE_SIZE, in) != NULL )
{
// Replace the commas by white space.
char* cp = line;
for ( ; *cp != '\0'; ++cp )
{
if ( *cp == ',' )
{
*cp = ' ';
}
}
// Now use sscanf to read the data.
// Always provide width with %s to prevent buffer overflow.
int n = sscanf(line, "%14s%14s%f%d%d",
class[i].first,
class[i].last,
&class[i].gpa,
&class[i].grev,
&class[i].greq);
// Use the data only if sscanf is successful.
if ( n == 5 )
{
// Use the data
}
}
Related
I am trying to read in data from a file that is formatted with ;.
The data will always be of like this:
char[];int;int%;int
The char[] can have any number of spaces and the % should be disregarded when reading the data.
I am using fscanf() (I am allowed to use only that) for reading the data from the file.
Right now my code for that part of it is:
fscanf(file, "%[^;]%d%d%d", f_name, &f_id, &f_score, &f_section_num) != EOF)
Is there a regex for what I need? Or, how do I correct my fscanf?
You can read the file using fscanf with this format string:
"%[^;];%d;%d%%;%d"
%[^;]: read up to first ;
;: ignore the ;
%d: read one integer
;: ignore the ;
%d: read one second integer
%%: ignore the %
;: ignore the ;
%d: read one third integer
Do not forget to test the number of successful conversions made by fscanf by testing fscanf(...) == 4
So code will looks like:
FILE *f = fopen(...);
char name[64];
int i, integers[3];
while (fscanf(f, "%[^;];%d;%d%%;%d", name, &integers[0], &integers[1], &integers[2]) == 4)
{
printf("name is %s\n", name);
for (i = 0; i < 3; ++i)
{
printf("i[%d] = %d\n", i, integers[i]);
}
}
fclose(f);
You could, alternatively, use strtok(). If, for example, you use a struct for each entry as follows,
typedef struct {
char name[64];
int id, score, section_num;
} entry_t;
the following would read each line of the file as follows.
char line[128] = {'\0'};
char *field = NULL;
entry_t entry;
while (fgets(line, sizeof(line), fp)) {
field = strtok(line, ";");
if (!field || strlen(field) > sizeof(entry.name)) continue;
strcpy(entry.name, field);
field = strtok(NULL, ";");
if (!field) continue;
entry.id = atoi(field);
field = strtok(NULL, ";%");
if (!field) continue;
entry.score = atoi(field);
field = strtok(NULL, ";");
if (!field) continue;
entry.section_num = atoi(field);
// Do whatever you need with the entry - e.g. print its contents
}
I have removed some necessary boilerplate code for brevity. See http://codepad.org/lg6BJ0hk for a full example.
You can use strtol() instead of atoi() if you need to check the results of the integer conversions.
The following code will allow you to read data separated by ; from your file:
char msg[100];
int a;
char b[100];
int c;
fscanf(fptr, "%[^;];%d;%[^;];%d", msg, &a, b, &c);
printf("%s\n %d\n %d\n %d\n", msg, a, atoi(b), c);
I'm a student, I am wondering...
How can I make a program that can Get some data from my text file to a variable on my program and print them
Example:
My Text File
I,Ate,Cookies
She,Drink,Coffee
Tom,Wears,Pyjamas
My code
main()
{
FILE *fp=fileopen("c:\\textfile.txt","r");
char name[20],action[20],item[20];
prinf("Enter name: \n");
scanf("%s",&name);
/* I dont Know what to do next */
}
I though about some checking code:
if (name==nametxt) /*nametxt is the first line on the text file */
{
printf("%s\n %s\n %s\n",name,action,item);
}
If the name is "I",the output would look like this :
Enter name:
I
I
Eat
Cookies
A help will satisfy my curiosity thanks in advance
You are reading characters from file until you receive new line character (\n) or fill an array, then you return characters stored in an array passed by caller.
From this returned array you may get separated values with strtok.
Repeat until you receive 0 from getline (Getline received EOF from file.)
Here is simple example with your own getline function which you may modify.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int getline(char s[],int lim, FILE * fp)
{
int c, i;
for (i=0; i < lim-1 && (c=fgetc(fp))!=EOF && c!='\n'; ++i)
{
s[i] = c;
}
if (c == '\n')
{
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
int main()
{
FILE * fp = fopen("c:\\textfile.txt", "r");
char line[100];
char * ptr;
while (getline(line, 100, fp))
{
ptr = strtok(line, ",");
while( ptr != NULL )
{
printf(" %s\n", ptr);
ptr = strtok(NULL, ",");
}
}
return 0;
}
Output
I
Ate
Cookies
She
Drink
Coffee
Tom
Wears
Pyjamas
Storing strings into variable isnt tough, here is an example
strcpy(name, ptr);
But be careful, writing outside of bounds have undefined behavior.
strncpy(name, ptr, 100); You can limit number of copied characters with strncpy, but be careful, this function is error-prone.
You can do like this,
Go on reading characters from a file, after every character is read compare with ',' character.
If the character read is ',' then you have finished reading the name, otherwise store it in a character array and continue reading the file.
Once you hit ',' character, terminate the character array with null character(Now you have a complete name with you).
Compare this character array with a string you receive as input using a strcmp(String compare function). If its it matches decide what you wanna do?
I hope i am clear.
There is different ways to read data from a FILE * in C :
You read only one character : int fgetc(FILE *fp);.
You read a whole line : char *fgets(char *buf, int n, FILE *fp); (take care to buf, it must point to allocate memory).
You read a formatted string, which is your case here : int fscanf(FILE *stream, const char *format, ...), it works like printf() :
This way :
char name[20], action[20], item[20];
FILE *f = fopen("myfile.txt", "r");
if (! f)
return;
if (3 == fscanf(f, "%19[^,\n],%19[^,\n],%19[^,\n]\n", name, action, item))
printf("%s %s %s\n", name, action, item)
%30[^,\n], here is used to read of whole object of your line, except , or \n, which will read item by item the content of your string.
start with like this
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DATA_FILE "data.txt"
#define LEN 19
#define SIZE (LEN+1)
//Stringification
#define S_(n) #n
#define S(n) S_(n)
enum { NOT_FOUND, FIND };
int pull_data(const char name[SIZE], char action[SIZE], char item[SIZE]){
int ret = NOT_FOUND;
FILE *fp = fopen(DATA_FILE, "r");//fileopen --> fopen
if(fp == NULL){
perror("fopen:");
exit(EXIT_FAILURE);
} else {
char nametxt[SIZE];
*action = *item = 0;
while(fscanf(fp, "%" S(LEN) "[^,],%" S(LEN) "[^,],%" S(LEN) "[^\n]%*c", //"%19[^,],%19[^,],%19[^\n]%*c"
nametxt, action, item) == 3){
if(strcmp(name, nametxt) == 0){//Use strcmp for comparison of strings
ret = FIND;
break;
}
}
}
fclose(fp);
return ret;
}
int main(void){
char name[SIZE], action[SIZE], item[SIZE];
printf("Enter name: \n");//prinf --> printf
if(scanf("%" S(LEN) "s", name) == 1){
if(pull_data(name, action, item) == FIND){
printf("%s\n%s\n%s\n", name, action, item);
} else {
printf("%s not found.\n", name);
}
}
}
I have a struct named excuses that has chars, I need to store at least 20 excuses. Then, I need to divide each word of each excuse in an array.
¿How i can do that?
#define excuseLength 256
typedef struct{
char sentence[excuseLength];
}excuse;
excuse listExcuses[20];
for (int listExcuses_i = 0; listExcuses_i < 20; listExcuses_i++)
{
char *input;
scanf("%s", input);
strcpy(listExcuses[listExcuses_i].sentence, input);
char* token = strtok(input, " ");
while(token != NULL){
printf("token: %s\n", token);
token = strtok(NULL, " ");
}
}
Here are some things you can add to your solution:
Check fgets() for return value, as it returns NULL on error.
If you decide to still use scanf(), make sure to use scanf("%255s", input) instead for char input[256]. Using the format specifier %255s instead of the simpe %s checks for excessive input. Overall, it just better to read input using fgets().
Remove '\n' character appended by fgets(). This is also good for checking that you don't enter more characters than the limit of 256 in input, and that your sentences don't have a trailing newline after each of them. If you don't remove this newline, then your strtok() delimiter would have to be " \n" instead.
#define constants in your code, and use const char* for string literals, such as the delimiter for strtok().
You can also add some code to check for empty inputs from fgets(). You could simply use a separate counter, and only increment this counter for valid strings found.
It's also strange to have struct with one member, usually structs contain more than one member. You could simply bypass using a struct and use a 2D char array declared as char listexcuses[NUMEXCUSES][EXCUSELENGTH]. This array can hold up to 20 strings, each of which has a maximum length of 256.
Here is some modified code of your approach:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define EXCUSELENGTH 256
#define NUMEXCUSES 20
typedef struct {
char sentence[EXCUSELENGTH];
} excuse;
int main(void) {
excuse listexcuses[NUMEXCUSES];
char input[EXCUSELENGTH] = {'\0'};
char *word = NULL;
const char *delim = " ";
size_t slen, count = 0;
for (size_t i = 0; i < NUMEXCUSES; i++) {
printf("\nEnter excuse number %zu:\n", count+1);
if (fgets(input, EXCUSELENGTH, stdin) == NULL) {
fprintf(stderr, "Error from fgets(), cannot read line\n");
exit(EXIT_FAILURE);
}
slen = strlen(input);
if (slen > 0 && input[slen-1] == '\n') {
input[slen-1] = '\0';
} else {
fprintf(stderr, "Too many characters entered in excuse %zu\n", count+1);
exit(EXIT_FAILURE);
}
if (*input) {
strcpy(listexcuses[count].sentence, input);
count++;
printf("\nTokens found:\n");
word = strtok(input, delim);
while (word != NULL) {
printf("%s\n", word);
word = strtok(NULL, delim);
}
}
}
return 0;
}
As you need to eventually store these tokens somewhere, you will need another form of storing this data. Since you don't know how many tokens you can get, or how long each token is, you may need to use something like char **tokens. This is not an array, but it is a pointer to a pointer. Using this would allow any number of words and any lengths of each word to be stored. You will need dynamic memory allocation for this. The answer in this post will help.
I changed the scanf for fgets and initialize the char input[256] and with that now it works!
#define excuseLength 256
#define numberExcuses 20
typedef struct{
char sentence[excuseLength];
}excuse;
excuse listExcuses[20];
for (int listExcuses_i = 0; listExcuses_i < numberExcuses; listExcuses_i++)
{
char input[256];
scanf("%s", input);
fgets(input, 256, stdin);
strcpy(listExcuses[listExcuses_i].sentence, input);
char* token = strtok(input, " ");
while(token != NULL){
printf("token: %s\n", token);
token = strtok(NULL, " ");
}
}
The input text file has some numbers per line, numbers are split by space. The first two lines only got one number, and the following lines got three. What I want to do is read each line of the input and store these numbers.
This is what I've got so far:
int
main(int argc, char *argv[]) {
int n = 0;
char buff[MAX_STRING_LEN]; //MAX_STRING_LEN is defined as 64
while (fgets(buff,MAX_STRING_LEN, stdin) != NULL) {
char temp;
if (n == 0) {
sscanf(buff, "%s", &temp);
int h_num = (int)temp;
} else if (n == 1) {
sscanf(buff, "%s", &temp);
int s_num = (int)temp;
} else {
sscanf(buff, "%s", &temp);
char *token;
token = strtok(&temp, " ");
int i = 0;
int a,b,c;
while (token != NULL) {
if (i == 0) {
a = (int)token;
token = strtok(NULL, " ");
} else if (i == 1) {
b = (int)token;
token = strtok(NULL, " ");
} else {
c = (int)token;
token = strtok(NULL, " ");
}
i++;
}
}
n++;
}
return 0;
}
The print statement I used to test my code is like:
printf("%d\n",h_num);
printf("%d\n%d\n%d\n",a,b,c);
I created a text file like this:
23
34
4 76 91
but the output is not what I expected, it's the address of the pointer I think. (I'm stuck with pointer again =( )
Could someone help me to point out what the problem is? Appreciate it.
In your code, I can see,
int h_num = (int)temp;
and
int s_num = (int)temp;
No, that is not how you convert an aphanumeric string to int.
You need to use strtol() for this purpose.
Then,
sscanf(buff, "%s", &temp);
is wrong. temp is a char, you got to use %c for that.
My suggestion for a better approach:
Read a complete line from file using fgets()
tokenize the input using strtok(), using space () as delimiter, then convert the token (if not NULL) to int using strtol()
continue untill the returned token is NULL
In this case, your code will be much more generic, as don't need to bother seperately about the number of ints present in each line.
I am trying to read and get the total of filesizes of multiple files from 1 line from stdin. The code below works perfectly if there is 1 file but if there are multiple it fails because it cannot distinguish when 1 file ends and the other begins. The filenames are separated by whitespace (ex: echo "file1.txt file2.txt"), can someone point me in the right direction to how I can evaluate each filename's size individually? The filesize function was not included for the sake of brevity
int main (int argc, char *argv[])
{
char tmpstring[1024];
const char* fileName;
off_t size;
char* pos;
int total = 0;
//read from stdin
while (fgets(tmpstring, 1024, stdin))
{
fileName = tmpstring;
if ((pos=strchr(fileName, '\n')) != NULL)
*pos = '\0';
printf("this is the name: %s\n", fileName); //#DEBUG
size = filesize(fileName);
total += size;
//} #DEBUG
}
printf("This is the total: %d\n", total); //#DEBUG
return -1;
}
How about using scanf instead:
int main() {
char buffer[1024];
int total = 0;
while (scanf("%1023s", buffer) == 1) {
printf("this is the name: %s\n", buffer);
total += filesize(buffer);
}
printf("This is the total: %d\n", total);
return 0; // You shouldn't return -1!
}
scanf first consumes leading whitespace, and then reads a sequence of non-whitespace characters. The return value 1 indicates that a string was successfully read (caveat: scanf actually returns the number of input items matched; see the manual!).
The maximum field width specifier (the 1023 in %1023s) is necessary to avoid a buffer overflow vulnerability. Had I omitted it, it would have been possible to feed a string much longer than 1023 characters to scanf. The extra character is required to store the null terminator.
Note: A (possibly undesirable) side-effect of this approach is that it's not necessary to enter all the file names on a single line. If you don't want this behavior, a modification of your initial approach does the trick:
int main(int argc, char *argv[]) {
char buffer[1024];
const char* fileName;
int total = 0;
char *pos;
// Read from stdin. You should do some error checking too.
fgets(buffer, sizeof buffer, stdin);
// Get rid of the trailing '\n'.
if ((pos = strchr(buffer, '\n')) != NULL)
*pos = '\0';
fileName = strtok(buffer, " ");
while (fileName) {
printf("this is the name: %s\n", fileName);
total += filesize(fileName);
fileName = strtok(NULL, " ");
}
printf("This is the total: %d\n", total);
return 0;
}
On a side note, you shouldn't be using int to represent file sizes. It is quite likely that int is only 32 bits on your machine, in which case even a few relatively small files could overflow it.