I'm writing a game in SDL2 for a school project, in C, I have a config that lists key-values pairs as such:
groundTiles: images/Overworld/groundTiles.png
and
cellHeight: 32
How should I go about parsing this data? Because my attempts result in the integers being read correctly but strings are either missing chars or are completely corrupt. I'm somewhat of a beginner to C, at least in terms of file i/o
I need another set of eyes on this code because I've spent too many hours on this already.
Could it have something to do with this struct in my header and how I'm using it to store temporary data?
typedef struct TileMapData_S
{
Uint32 col, row, cellWidth, cellHeight, numCells;
char *mapName;
char *emptyTileName;
Bool flag;
SDL_Color *colors;
Tile* tileTypes;
char *colorMap;
}TileMapData;
I've tried making it an unnamed struct in the function, then the source. No luck. I tried just not using a struct and fscanf'ing each piece of data into a separate variable. Same thing, no luck. If I did fscanf(file, "%s %s", buf, temp) with temp being the value of the key I'm parsing, then I get the first encounter of the string I'm looking for, then it copies itself to the other two char* that are holding the names of my sprites/files.
EDIT: This is my attempt based on comments, which does not work, any insight would be appreciated
while (!data->flag)
{
while (tempString != EOF)
{
tempString = strtok(buf, " \n");
if (strcmp(tempString, "width:") == 0)
{
tempString = strtok(buf, "\n\0 ");
map->numColumns = atoi(tempString);
continue;
}
.
.
.
if (strcmp(tempString, "groundTiles:") == 0)
{
data->mapName = strtok(buf, "\n\0 ");
data->mapName = tempString;
if (data->mapName != NULL)
{
data->flag = true;
}
else
{
data->flag = false;
}
continue;
}
.
.
.
tempString = fgets(buf, sizeof(buf), file);
slog(buf);
}
rewind(file);
}
I was expecting to get the string I wanted, without the whitespace/null-terminating char, but ended up with an infinite loop
END EDIT
I expect that when I parse groundTiles: images/Overworld/groundTiles.png
using fscanf(file, "%s", buf), doing strcmp on that and a known string (groundTiles:), then a second fscanf should provide the string images/Overworld/groundTiles.png
Related
I am trying to read each line of a file and store binary values into appropriate variables.
I can see that there are many many other examples of people doing similar things and I have spent two days testing out different approaches that I found but still having difficulties getting my version to work as needed.
I have a txt file with the following format:
in = 00000000000, out = 0000000000000000
in = 00000000001, out = 0000000000001111
in = 00000000010, out = 0000000000110011
......
I'm attempting to use fscanf to consume the unwanted characters "in = ", "," and "out = "
and keep only the characters that represent binary values.
My goal is to store the first column of binary values, the "in" values into one variable
and the second column of binary values, the "out" value into another buffer variable.
I have managed to get fscanf to consume the "in" and "out" characters but I have not been
able to figure out how to get it to consume the "," "=" characters. Additionally, I thought that fscanf should consume the white space but it doesn't appear to be doing that either.
I can't seem to find any comprehensive list of available directives for scanners, other than the generic "%d, %s, %c....." and it seems that I need a more complex combination of directives to filter out the characters that I'm trying to ignore than I know how to format.
I could use some help with figuring this out. I would appreciate any guidance you could
provide to help me understand how to properly filter out "in = " and ", out = " and how to store
the two columns of binary characters into two separate variables.
Here is the code I am working with at the moment. I have tried other iterations of this code using fgetc() in combination with fscanf() without success.
int main()
{
FILE * f = fopen("hamming_demo.txt","r");
char buffer[100];
rewind(f);
while((fscanf(f, "%s", buffer)) != EOF) {
fscanf(f,"%[^a-z]""[^,]", buffer);
printf("%s\n", buffer);
}
printf("\n");
return 0;
}
The outputs from my code appear as follows:
= 00000000000,
= 0000000000000000
= 00000000001,
= 0000000000001111
= 00000000010,
= 0000000000110011
Thank you for your time.
The scanf family function is said to be a poor man'parser because it is not very tolerant to input errors. But if you are sure of the format of the input data it allows for simple code. The only magic here if that a space in the format string will gather all blank characters including new lines or none. Your code could become:
int main()
{
FILE * f = fopen("hamming_demo.txt", "r");
if (NULL == f) { // always test open
perror("Unable to open input file");
return 1;
}
char in[50], out[50]; // directly get in and out
// BEWARE: xscanf returns the number of converted elements and never EOF
while (fscanf(f, " in = %[01], out = %[01]", in, out) == 2) {
printf("%s - %s\n", in, out);
}
printf("\n");
return 0;
}
So basically you want to filter '0' and '1'? In this case fgets and a simple loop will be enough: just count the number of 0's and 1's and null-terminate the string at the end:
#include <stdio.h>
int main(void)
{
char str[50];
char *ptr;
// Replace stdin with your file
while ((ptr = fgets(str, sizeof str, stdin)))
{
int count = 0;
while (*ptr != '\0')
{
if ((*ptr >= '0') && (*ptr <= '1'))
{
str[count++] = *ptr;
}
ptr++;
}
str[count] = '\0';
puts(str);
}
}
I'm trying to use C to read a CSV file, iterate line by line (until EOF), and delimit/split each line by the comma. Then I wish to separate each column into "bins" and put add them to a struct (which isn't shown here; I defined it in a helper file) based on type.
For example, if I have 1,Bob, I'd like to split 1 and Bob into two variables. Here's what I've written so far.
void readFile(char file[25]) {
FILE *fp;
char line[1000];
fp = fopen(file, "r"))
while(fgets(line, 1000, fp)) {
char* tmp = strdup(line);
char* token;
while((token = strsep(&tmp, ","))) {
printf("%s\n", token); // I want to split token[0] and token[1]
}
}
fclose(fp);
}
T he above code does compile and run. I just don't know how to access each split of the token, like token[0] or token[1]. In python, this would be simple enough. I could just access 1 using token[0] and Bob using token[1] for each line. But here in C, I can't do that.
For testing purposes, all I'm doing right now is printing each line (in the second while loop), just to see how each split looks. I haven't implemented the code where I put each split line into its respective struct member.
I've searched Stack Overflow and found a multitude of threads on this topic. None of them seemed to help me except for this one, which I have drawn from. But I wasn't able to get the storing of split columns working.
In python, this would be simple enough. I could just access 1 using token[0] and Bob using token[1] for each line. But here in C, I can't do that.
Yes, you can, if only you define the array.
while (fgets(line, sizeof line, fp))
{
char *tmp = strchr(line, '\n');
if (tmp) *tmp = '\0'; // remove the '\n'
tmp = strdup(line);
#define MAXCOLUMNS 2
char *token[MAXCOLUMNS];
int c = 0;
while (tmp)
{
if (c == MAXCOLUMNS) puts("too many columns"), exit(1);
token[c++] = strsep(&tmp, ",");
}
if (1 <= c) printf("column 1: %s\n", token[0]);
if (2 <= c) printf("column 2: %s\n", token[1]);
// ONLY if the line's tokens are no longer needed:
free(*token);
}
I am currently trying to understand how to go through a .txt file in C and I think I have mostly everything worked out but what I need to do is kind of confusing. I need to create an array of Pointers to point to structs.
Each line in my .txt file should have information corresponding to a single struct. Each line should start with a name followed by some float values.
My question is, when I read the lines and parse them using strtok first, how would I get that information in a struct?
second how would I then make the sample pointer at index i point to the struct?
I tried doing the name seperate from the numbers since the numbers need their own special atof conversion since initially it will be a string. However I think this is probably incorrect since I want to read multiple lines, the code I have before the while loop for obtaining the name will only run once so any following lines will not have the name seperated. I can technically delimit my text file as I choose, so maybe I can just seperate the name with a semicolon and the rest spaces?
If this question seems confusing its probably because I am over thinking
Should I be declaring a struct such as : Sample tmp;
I've been reading examples but I can't figure out how to put the information together. Let me know if I declared my array of pointers incorrectly... Which I think I did. I think my the line that says:
sample arr[SIZE] = {NULL}; might be incorrect but I am not sure. if you can help me work out the logic behind all this I would appreciate it. Thanks.
typedef struct sample{
char* name;
int list_len;
float* value_list;
}sample;
void read_and_parse(){
const int SIZE = 1024;
sample* sample = (sample*)malloc(sizeof(sample); //pointer allocation?
FILE* fin;
fin = fopen("record.txt", "r");
if (fin == NULL) {
printf("record.txt could not be opened \n");
exit(1);
}
else {
int i= 0;
sample arr[SIZE] = {NULL}; //here I try to make the array of pointers
char linebuf[SIZE];
token = strtok(linebuf, " "); //grab the first item
while (fgets(linebuf, SIZE, fin) && i<SIZE) {
arr[i] = malloc(sizeof(sample));
arr[i.name] = token;
token = strtok(NULL, " ");
// now parse the linebuf and fill arr[i] with it
i++;
}
Edited: 11/02/2017
any print statements you see are just silly markers I placed for testing and recognizing what is running when I finally get this code compiled
Here is a much better edited version of the code. I think it should work now.
typedef struct sample{
char* name;
int list_len;
float* value_list;
}sample;
void read_and_parse(FILE **fin, sample* arr[]){
const int SIZE = 1024;
if (*fin == NULL) {
printf("record.txt could not be opened \n");
exit(1);
}
else {
printf("successfully opened file\n");
char linebuf[SIZE];
while ( fgets(linebuf, SIZE, fin) ) {
arr[i] = malloc(sizeof(sample));
int floats_per_line = 0;
while(linebuf[i]){
if(linebuf[i] == ' ');
++floats_per_line;
}
arr[i]->list_len = values_per_line;
arr[i]->value_list = (float*)malloc(sizeof(float)*floats_per_line);
arr[i]->name = strdup(strtok(linebuf, ' '));
char* tok;
int j = 0
while(tok = strtok(NULL, ' ')){
arr[i]->value_list[j] = atof(tok);
++j
}
i++;
}
}
fclose(fin);
}
How would I read a line, parse the information and then attribute it to a struct ?
Read with fgets() which converts a line of file input into a string. OP does that well. Then parse the string.
when I read the lines and parse them using strtok first, how (to) get that information in a struct?
Should I be declaring a struct such as : sample tmp;
Pass the string to a helper function to parse it into a sample that can hold any input. So the pointer members of tmp need to point to maximal space.
char name[SIZE];
char f[SIZE/2];
sample tmp = { name, 0, f };
while (i<SIZE && fgets(linebuf, SIZE, fin)) {
if (sample_parse(&tmp, linebuf) == NULL) {
break; // Parsing failed for some reason, perhaps an error message?
}
// Now populate arr[i] with right-sized memory allocations
arr[i].name = strdup(tmp.name); // ToDo: add NULL check
arr[i].list_len = tmp.list_len;
size_t f_size = sizeof *(tmp.value_list) * tmp.list_len;
arr[i].value_list = malloc(f_size); // ToDo: add NULL check
memcpy(arr[i].value_list, tmp.value_list, f_size);
i++;
}
so maybe I can just separate the name with a semicolon and the rest spaces?
Yes. Also allow other white-spaces too.
if I declared my array of pointers incorrectly.
Code does not have an array of pointers anywhere.
Recommend using size_t for array size type.
typedef struct sample {
char* name;
// int list_len;
size_t list_len;
float* value_list;
} sample;
Some untested code for parsing. Parse the line with strtok(). Further parse the number tokens with strtof().
#define sample_NAME_DELIMITER ":"
#define sample_NUMBER_DELIMITER " \n\t\r"
// parse for a name and then 0 or more numbers
static sample *sample_parse(sample *dest, char *linebuf) {
char *s = strtok(linebuf, sample_NAME_DELIMITER);
if (s == NULL) {
return NULL; // no name - TBD on if this is allowed
}
strcpy(dest->name, s);
size_t i = 0;
while ((s = strtok(NULL, sample_NUMBER_DELIMITER)) != NULL) {
char *endptr;
dest->value_list[i] = strtof(s, &endptr);
if (s == endptr || *endptr) {
// conversion failed or extra junk
break;
}
i++;
}
dest->list_len = i;
return dest;
}
My program is supposed to be able to create new structures and store them in an array, however, the commands for storing and displaying pose difficulty.
To create a new variable struct in the array, the user inputs "set varname varcontents
To display the contents of a variable, the user inputs "set varname"
To display all variables, the user inputs "set"
I can't quite figure out how to check if there are multiple strings ("set" "varname" "varcontents") or if there is only "set"
char command[2][5] = { "set", "clear"};
printf("prompt> ");
scanf("%s",inputString);
if(strncmp(inputString,command[0],5) == 0 )
{
//code to create new struct, display structs etc...
}
else if(strncmp(inputString,command[1],5) == 0 )
{
//code to clear struct
}
Right now the if loop only passes if the user inputs "set".
I could probably take the comparison of the first few letters, and then take the full comparison and subtract the first few characters to generate the name of the new struct, but this seems too complicated, there must be an easier solution.
Any help is appreciated!
You could split the sentence into array of words and you can compare those words and run your functions.Give a try and If u want i can post the code.
There are some Problems in your code. First of all, scanf won't read "set variablename variablevalue", because it skips on whitespaces. It's not considered safe anyway, since it allows buffer overflows easily - specially beginners should rather use fgets().
But the main Problem is somewhere else - Consider the following snippet:
scanf("%s", inputString);
What would happen if you enter: 'set xyz 12'? scanf would just read 'set' and the other Input will be ignored. So there's no point in checking against the other parameters 'xyz' and '12'.
Maybe you want to use something like
scanf("%s %s %s", inputString1, inputString2, inputString3);
but I would advise against it and rather use fgets(). Apart from that you just Need simple pointer arithmetic to skip over the characters which you already processed.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (){
char* command[] = { "set", "clear"};
char input[256], varName[256], varContents[256];
char* pointer = input;
int i = 0;
printf("prompt>");
fgets(input, 255, stdin);
if(strncmp(pointer, command[0], strlen(command[0])) == 0 ){
pointer += strlen(command[0]) + 1;
while(*pointer != ' ' && *pointer != '\n'){
varName[i] = *pointer;
pointer++;
i++;
}
if(*pointer == '\n'){ printf("Not enough arguments"); return 1; }
varName[i] = '\0';
pointer++;
i = 0;
while(*pointer != ' ' && *pointer != '\n'){
varContents[i] = *pointer;
pointer++;
i++;
}
varContents[i] = '\0';
//code to create new struct, display structs etc...
printf("Set VarName: %s VarContents: %s\n", varName, varContents);
}else if(strncmp(pointer, command[1], strlen(command[1])) == 0 ){
//code to clear struct
}
return 0;
}
You are only reading one string, you should use two scanf's to read two
char arg0[30], arg1[30];
while (scanf("%s %s", arg0, arg1) < 2);
That will read until both strings are entered.
Hope this helps.
I am currently trying to scan a single line in from a file but having a snag at strings.
This is the example line my professor told me to work on.
enum status{MEM,PREP,TRAV}
union type { double int day, char* title, float cost}
13953 P 12 26 2011 1 5 2012 2 A 3.30 249.00 A 2.0 148.00 MEM Cuba Christmas 3 0 2 Sierra Del Rosario, Cuba
I'm fine with everything accept at the point (MEM Cuba Christmas) when I'm scanning it in from a FILE. I read the first part of the data just using fscanf(), but MEM is a enumerated type with a union type that dictates the following input. My problem is with the syntax of the scanning. I tried using getline starting at MEM but I hit snags with the tokenizing since the city / country can have spaces. Not sure what other scans to use I was looking at sscanf() but wasn't sure if it works with files.
UPDATED:
int main(void);
{
int m, length = 100;
char *word, file_name[100];
FILE *file_point
printf("Please enter file name with .txt extension:");
scanf("%s", file_name);
file_point = fopen(file_name,"r");
while (fscanf(file_point, "%d", &m) != EOF)
{
temp.dest_code = m;
fscanf(file_point, " %c %d %d %d %d %d %d %d",
&temp.area_code,
&temp.Smonth, &temp.Sday, &temp.Syear,
&temp.Emonth, &temp.Eday, &temp.Eyear,
&temp.leg_num);
for (n=0; n < temp.leg_num; n++)
{
fscanf(file_point," %c %f %f",
&temp.tleg[n].travel_type,
&temp.tleg[n].travel_time,
&temp.tleg[n].cost);
}
fscanf(file_point," %d %d %d ",
&temp.adult,
&temp.child,
&temp.infant);
temp_name = (char *)malloc(length + 1);
getline (&temp_name, &length, file_point);
word = strtok(temp_name, ",");
temp.dest_name=(char *)malloc(strlen(word)+1);
strcpy(temp.dest_name, word);
word = strtok(NULL, ",");
temp.dest_country=(char *)malloc(strlen(word)+1);
strcpy(temp.dest_country,word2);
printf("name:%s country:%s\n", temp.dest_name, temp.dest_country);
printf("adult:%d , child:%d , infant:%d \n", temp.adult, temp.child, temp.infant);
}
}
This was the code I was using as a base that I came up with but not sure how to handle the enumerated and union. I was thinking of doing something like:
getline(&status, &length, file_point);
but how do I convert string to integer or float?
If I understand your problem properly (I'm not sure I do), then you face the problem of seeing 'MEM' (or 'PREP' or 'TRAV') as a string in the input, and you have to understand how to handle the following data. The enum suggests that you might want to convert the string MEM to the value of MEM in the enumeration.
It is hard to fully automate such a conversion. It would be simplest simply to recognize the strings and decide what to do based on the string:
if (strcmp(found_string, "MEM") == 0)
...do the MEM stuff...
else if (strcmp(found_string, "PREP") == 0)
...do the PREP stuff...
else if (strcmp(found_string, "TRAV") == 0)
...do the TRAV stuff...
else
...report unknown type code...
However, you can create a structure to handle the conversion from string to enumeration value.
struct StateConv
{
const char *string;
enum state number;
};
static struct StateConv converter[] =
{
{ "MEM", MEM },
{ "PREP", PREP },
{ "TRAV", TRAV },
};
enum { NUM_STATECONV = sizeof(converter) / sizeof(converter[0]) };
enum state state_conversion(const char *string)
{
for (int i = 0; i < NUM_STATECONV; i++)
{
if (strcmp(string, converter[i].string) == 0)
return(converter[i].number);
}
fprintf(stderr, "Failed to find conversion for %s\n", string);
exit(1);
}
You need a better error handling strategy than 'exit on error'.
Your scanning code will need to read the word, and then call state_conversion(). Then depending on what you get back, you can read the remaining (following) data in the correct way for the state you were given.
No, you can't do that in the way you are trying. MEM in your file is a string type, you need to parse it like you parse a string and then set the value of your enum according to that string.
For example, when you want to parse your status type (MEM,PREP,TRAV):
char typeBuffer[6];
fscanf(file_point,"%5s",typeBuffer);
Then manually compare the content of typeBuffer:
status stat;
if (strcmp(typeBuffer, "MEM") == 0){
stat = MEM;
}
The conversion between string type and enum cannot be implicit.