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);
Related
So, I was trying to write a program to take the first column of a CSV file and copy that do a string, but it's not going well. I put the code and the CSV file and I really appreciate any help.
int main () {
FILE *fp;
fp = fopen("filePATH", "r");
char column[80];
int line_n = 0;
char ch;
while ((ch = fgetc(fp)) != EOF) {
fgets(column, sizeof column, fp);
for (int i = 0; i < sizeof column; ++i){
fscanf(fp, "%[^;]", column);
}
printf("%s \n", column);
}
fclose(fp);
return 0;
}
CSV file:
2;51.5;144.0;24.80
5;62.3;157.0;25.30
10;52.8;141.0;26.60
10;34.5;120.0;24.00
1;41.6;131.0;24.20
5;49.0;144.0;23.80
6;47.1;142.0;23.50
2;51.8;144.5;24.80
1;55.6;135.0;30.50
9;51.9;150.0;23.10
9;48.5;139.0;25.10
The output I have is:
5
10
10
1
5
6
2
1
9
9
48.5;139.0;25.10
So, I don't understand why the program shows me the first column but copies only the last line for the string column.
To check the string column, I used:
char copy[20];
strncpy(copy, column, 18);
printf("%s ", copy);
And the output is:
48.5;139.0;25.10
Almost certainly you want to restructure the loops, but here are some minimal changes to your code that might be instructive:
#include<stdio.h>
char sample_input[] =
"2;51.5;144.0;24.80\n"
"5;62.3;157.0;25.30\n"
"10;52.8;141.0;26.60\n"
"10;34.5;120.0;24.00\n"
"1;41.6;131.0;24.20\n"
"5;49.0;144.0;23.80\n"
"6;47.1;142.0;23.50\n"
"2;51.8;144.5;24.80\n"
"1;55.6;135.0;30.50\n"
"9;51.9;150.0;23.10\n"
"9;48.5;139.0;25.10\n"
;
int
main(void)
{
FILE *fp = fmemopen(sample_input, sizeof sample_input, "r");
char column[80];
int line_n = 0;
int ch; /* fgetc returns an int. */
while( (ch = fgetc(fp)) != EOF ){
/* put ch back so it can be read by scanf */
ungetc(ch, fp);
/* This fgets does not seem to serve any purpose.
/* fgets(column, sizeof column, fp); */
ch = ';';
while( ch == ';' && fscanf(fp, "%79[^;\n]", column) == 1 ){
ch = fgetc(fp); /* Consume the ; or \n */
printf("%s%c", column, ch);
}
}
fclose(fp);
return 0;
}
It would probably be better to use the fgets to read each line of data and then use sscanf to parse the line, but I wanted to show that your current code can be made to (mostly) work with minimal changes. Note that the variable used to store the value returned by fgetc must be int rather than char in order to properly compare to EOF. Also, whenever you use %s or %[] in scanf, it is best to add a width modifier to prevent a buffer overflow.
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
}
}
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'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);
}
the following code that I wrote is supposed to transform a line taken from a file like the following:
(3670, 1882) (1574, 7255) (4814, 8566) (1609, 3153) (9725, 13468) (8297, 3006) (9091, 6989) (8521, 10432) (14669, 12201) (4203, 9729) (469, 2444) (10107, 8318) (1848, 13650) (5423, 847) (11755, 8827) (4451, 4495) (11645, 1670) (10937, 5692) (14533, 13696) (7291, 12158) (1891, 2405) (1776, 4971) (2486, 2499) (13389, 236) (8533, 7531) (10618, 10288) (9119, 11226) (9429, 6622) (12380, 9516) (1698, 5828) (8369, 5101) (11341, 13530) (11955, 2335) (6249, 14435) (9373, 6921) (2977, 2294) (57, 14558) (280, 12847) (13846, 11748) (428, 9004)
into a valid 2d matrix.
{{3670, 1882},{1547, 7255}...}
I'm a good "pythoner" and I'd be able to do that in one line. I wanted to try to solve the same problem in c (note that I started to mess around with c today); my attempt is the following (and the result is quite random/wrong):
FILE *fp;
fp=fopen(argv[1], "rt");
if ( fp != NULL )
{
char line [1000]; //this is ugly, isn't this?
while ( fgets ( line, sizeof line, fp ) != NULL ) // read a line
{
line[(strlen(line)-1)] = '\0';
//line[(strlen(line)-2)] = '\0';
char* p;
p = strtok(line, ",)( ");
int elements[100][2]; //even uglier than before?
int binpos=0;
int pos=0;
while (p != NULL)
{
if (p!=NULL){
if (binpos==0){
elements[pos][binpos]=atoi(p);
binpos=1;
}else{
p[(strlen(p)-1)] = '\0'; //remove the comma
elements[pos][binpos]=atoi(p);
pos++;
binpos=0;
}
}
p = strtok(NULL, ",)( ");
}
int it;
for (it=0; it<pos; it++){
printf("(%d, %d)\n",elements[it][0],elements[it][1]);
}
return 0;
}
}
Can someone please tell me how to correct my mess? :)
You should only be using fgets to read lines if lines are relevant to what you are parsing. In your case it looks like line endings are irrelevant, so you're better off just using scanf:
printf("{");
const char *sep = "";
int a, b;
while (fscanf(fp, "(%d,%d)", &a, &b) == 2) {
printf("%s{%d, %d}", sep, a, b);
sep = ", "; }
printf("}");