Store String of Integers into an Array - c

void ReadReferenceStream(char * filename) {
char buf[MAXCHAR];
int i;
char* np;
inputfile = fopen(filename, "r");
if(inputfile == NULL)
printf("File is empty!\n");
while((fgets(buf,MAXCHAR,inputfile)) != NULL) {
if(buf[0] == '#' || buf[0] == '\n')
continue;
}
np = strtok(buf," ");
Pages = malloc(atoi(np)*sizeof(int));
That's all I have so far.
I need to first dynamically allocate space for my array (the variable 'np' is how many integers have to be in the array 'Pages'), which I hope I did correctly.
Then, I have skip any lines in the input text file that start with # or the NULL character.
Finally, I need to store the integers into the array Pages, with the exception of the first integer, which is the value for 'np'.
How can I alter my code to get it to work exactly as I have mentioned. I got most of it, I am just struggling to get it to put the integers into the array.

Given what you have so far, and assuming Pages is defined like int *Pages, just read in the remaining integers:
for (i = 0; np = strtok(NULL, " "); ++i) Pages[i] = atoi(np);

Related

C How to properly fscanf with numbers and strings?

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

C Reading a file of digits separated by commas

I am trying to read in a file that contains digits operated by commas and store them in an array without the commas present.
For example: processes.txt contains
0,1,3
1,0,5
2,9,8
3,10,6
And an array called numbers should look like:
0 1 3 1 0 5 2 9 8 3 10 6
The code I had so far is:
FILE *fp1;
char c; //declaration of characters
fp1=fopen(argv[1],"r"); //opening the file
int list[300];
c=fgetc(fp1); //taking character from fp1 pointer or file
int i=0,number,num=0;
while(c!=EOF){ //iterate until end of file
if (isdigit(c)){ //if it is digit
sscanf(&c,"%d",&number); //changing character to number (c)
num=(num*10)+number;
}
else if (c==',' || c=='\n') { //if it is new line or ,then it will store the number in list
list[i]=num;
num=0;
i++;
}
c=fgetc(fp1);
}
But this is having problems if it is a double digit. Does anyone have a better solution? Thank you!
For the data shown with no space before the commas, you could simply use:
while (fscanf(fp1, "%d,", &num) == 1 && i < 300)
list[i++] = num;
This will read the comma after the number if there is one, silently ignoring when there isn't one. If there might be white space before the commas in the data, add a blank before the comma in the format string. The test on i prevents you writing outside the bounds of the list array. The ++ operator comes into its own here.
First, fgetc returns an int, so c needs to be an int.
Other than that, I would use a slightly different approach. I admit that it is slightly overcomplicated. However, this approach may be usable if you have several different types of fields that requires different actions, like a parser. For your specific problem, I recommend Johathan Leffler's answer.
int c=fgetc(f);
while(c!=EOF && i<300) {
if(isdigit(c)) {
fseek(f, -1, SEEK_CUR);
if(fscanf(f, "%d", &list[i++]) != 1) {
// Handle error
}
}
c=fgetc(f);
}
Here I don't care about commas and newlines. I take ANYTHING other than a digit as a separator. What I do is basically this:
read next byte
if byte is digit:
back one byte in the file
read number, irregardless of length
else continue
The added condition i<300 is for security reasons. If you really want to check that nothing else than commas and newlines (I did not get the impression that you found that important) you could easily just add an else if (c == ... to handle the error.
Note that you should always check the return value for functions like sscanf, fscanf, scanf etc. Actually, you should also do that for fseek. In this situation it's not as important since this code is very unlikely to fail for that reason, so I left it out for readability. But in production code you SHOULD check it.
My solution is to read the whole line first and then parse it with strtok_r with comma as a delimiter. If you want portable code you should use strtok instead.
A naive implementation of readline would be something like this:
static char *readline(FILE *file)
{
char *line = malloc(sizeof(char));
int index = 0;
int c = fgetc(file);
if (c == EOF) {
free(line);
return NULL;
}
while (c != EOF && c != '\n') {
line[index++] = c;
char *l = realloc(line, (index + 1) * sizeof(char));
if (l == NULL) {
free(line);
return NULL;
}
line = l;
c = fgetc(file);
}
line[index] = '\0';
return line;
}
Then you just need to parse the whole line with strtok_r, so you would end with something like this:
int main(int argc, char **argv)
{
FILE *file = fopen(argv[1], "re");
int list[300];
if (file == NULL) {
return 1;
}
char *line;
int numc = 0;
while((line = readline(file)) != NULL) {
char *saveptr;
// Get the first token
char *tok = strtok_r(line, ",", &saveptr);
// Now start parsing the whole line
while (tok != NULL) {
// Convert the token to a long if possible
long num = strtol(tok, NULL, 0);
if (errno != 0) {
// Handle no value conversion
// ...
// ...
}
list[numc++] = (int) num;
// Get next token
tok = strtok_r(NULL, ",", &saveptr);
}
free(line);
}
fclose(file);
return 0;
}
And for printing the whole list just use a for loop:
for (int i = 0; i < numc; i++) {
printf("%d ", list[i]);
}
printf("\n");

Returning a array of structs

I am trying to figure out how to modify my code to actually allow for me to create a array of structs in my readFile and then return the array to the main.
This is my data struct
struct data{
char *model;
float engineSize;
int cost;
char *color;
};
This is my current setup of my readFile function and then the call that I use currently for this function.
struct data * readFile(){
FILE *fp;
int c;
int count = 0;
char *line = NULL;
size_t len = 0;
fp = fopen("hw3.data", "r");
while ((c = fgetc(fp)) != EOF){
count++;
}
if (feof(fp)){
rewind(fp);
struct data *vehicles = malloc((sizeof(struct data))* count);
count = 0;
char *token = NULL;
while (getline(&line, &len, fp)!= -1){
printf("%s", line);
token = strtok(line, " ");
vehicles[count].model = token;
token = strtok(NULL, " ");
vehicles[count].engineSize = atof(token);
token = strtok(NULL, " ");
vehicles[count].cost = atoi(token);
token = strtok(NULL, " ");
vehicles[count].color = token;
}
}
}
This is the main where I have my menu and where I will do my call for my readFile function.
int main(){
int check = 1;
int input;
while (check == 1){
printf("Enter a value corresponding to a option on the menu below\n\n");
printf("1. Sort data by the float value & print high to low\n");
printf("2. Sort data by the float value & print low to high\n");
printf("3. Sort data by the int value & print high to low\n");
printf("4. Sort data by the int value & print low to high\n");
printf("5. Exit\n\n");
printf("Enter a value corresponding to the above menu\n");
scanf("%d", &input);
//readFile()
if(input == 1 || input == 2 || input == 3 || input == 4 || input == 5){
if (input == 5){
exit(0);
}if (input == 1){
//sort float high to low
}if (input == 2){
//sort float low to high
}if (input == 3){
//sort int value high to low
}if (input == 4){
//sort int value low to high
}
}else{
printf("Enter a correct value for the menus above\n\n" );
}
readFile();
}
}
Thanks
It's almost correct, the idea is OK but there are a few issues:
while ((c = fgetc(fp)) != EOF){
count++;
}
That counts the number of bytes, I think based on the later code you want the
number of lines.
while ((c = fgetc(fp)) != EOF){
if(c == '\n')
count++;
}
would give you the number of lines.
Down there
token = strtok(line, " ");
vehicles[count].model = token;
...
token = strtok(NULL, " ");
vehicles[count].color = token;
is valid, but perhaps not what you want. strtok returns on success line + some_offset so if later you need to
add more characters to vehicles[i].mode or vehicles[i].color, you might
overwrite memory. vehicles[i].color is only at an offset of
vehicles[i].model. If you even want to reallocate, realloc will fail,
because you wouldn't be reallocation at the beginning of the requested memory
block. Also by doing this you will lose the beginning of the requested memory,
it will leak memory, because you cannot free it (free(vehicles[i].color) is
not valid)1.
Another problem is that only the initial line woul have the correct amount of
allocated memory and if you call getline with a non NULL pointer and
non-zero length, getline will reallocate memory if necessary and update the
pointer and the length. If the reallocation returns the same address, then your
previous values are going to be overwritten. If the reallocation returns a
different address, you previous pointer will become invalid.
I'd suggest (and I think it is the only safe way here) that you do a copy of token with strdup (if available, or malloc+strcpy)
and after that do:
while (getline(&line, &len, fp)!= -1){
// the strtok calls
...
free(line);
line = NULL;
len = 0;
}
In this way your code won't leak memory and you would not overwrite memory.
edit
I should instead be setting the values of model and color with strcpy instead
You can use strcpy, but you would need to allocate memory first, because
model and color are just pointers. The malloc call only reserved memory,
it does not initialize it. So just doing
strcpy(vehicles[count].model, token);
would be wrong, because it you would try to copy something on an undefined
location. That's what I mean with
I'd suggest (and I think it is the only safe way here) that you do a copy of token with strdup (if available, or malloc+strcpy)
vehicles[count].model = malloc(strlen(token) + 1);
if(vehicles[count].model == NULL)
{
// error handling
// for example
// free everything and return
return NULL;
}
strcpy(vehicles[count].model, token);
The function strdup essentially does that: malloc + strcpy, so if your
system has strdup you could do it like this:
vehicles[count].model = strdup(token);
if(vehicles[count].model == NULL)
{
// error handling
// for example
// free everything and return
return NULL;
}
Another option would be to change your struct and instead of having pointers to
char, use char arrays:
struct data{
char model[100];
float engineSize;
int cost;
char color[100];
};
Now you can save strings with maximal length of 99 chars (which should be enough
for a model name and color) and just use strncpy instead, without the need of
extra memory allocation:
strncpy(vehicles[count].model, token, sizeof vehicles[count].model);
// making sure to terminate the string
vehicles[count].model[sizeof(vehicles[count].model) - 1] = 0;
Also I just haven't had a chance to change the code for free (line) line =null and len =0
I don't know what you mean by that. Just add the lines after
vehicles[count].color = token;
before the end of the while loop.
So then also I should be using the get line like I was in the second iteration through the file because I am currently over allocating?
The second loop is fine, the problem is that you are assigned the same (+
offsets) memory locations to different pointers and when getline reallocates
and gets a different address, the previous pointer becomes invalid. That's why
free(line);
line = NULL;
len = 0;
is important and you definitevly should do that.
To summerize: Your loop is fine, but you need to make these changes:
make copies of token or change the struct to use char arrays
add the
free(line);
line = NULL;
len = 0;
lines at the end of the loop and you'll be fine.
fotenotes
1vehicles[i].mode would only point at the beginning of the memory
block if and only if the line doesn't start with an empty space. As you are
reading a file, you don't have any guarantee that this is true. And even if it's
true, I wouldn't count on that. Better doing the safe thing here and make a
copy. free(vehicles[i].color) is definitely wrong.

Getting weird output when concatenating a string and copying it into an array

I'm working on a function that takes characters from a file in amounts specified by the user and concatenates them into a string that will be stored into an array. The user would be prompt for a directory and amount of characters to be concatenated. However, when I tried doing this and printed out the results I got weird symbols and numbers like so;
#ؙ>\212\377`\366\277_\377g
Could anyone shed some light to why this might be happening. Thank you. Info: this is in main() right now, fileNames is the list of file names within the directory.
/*struct def*/
typedef struct arr{
char *wordd;
int indx;
}ARR;
/* Dynamic array of structs*/
ARR arr[COUNTER];
char wordConct[81];
for (i = 0 ; i < count; i++) {
sprintf( fullName, "%s/%s", directoryName, fileNames[i]); //DIRECTORY/FILENAME FORMAT
/*Read in from file*/
inFile = fopen( fullName, "r");
assert( inFile);
/* This loop concatenates input then stores in array */
while( fscanf( inFile, "%c", word) != EOF) {
if (isalnum(*word) != 0) { // Only goes through if alphanumeric
//nChar is user specified number of characters to be concatenated
if (j <= nChar) {
strcat(wordConct, word); //concatenates
j++; //add 1 to j to count until nChar is reached
}
else {
//copy concatenated word into the array once nChar is reached
strcpy(arr[j].wordd, wordConct);
}
j = 0; //reset back to zero for next iteration
}
}
}

Read file and store into array

I am trying to read the file and get file content and store it in 2 element array [0]: is for content [1]: is for NULL value. This code is working correctly when just I want to print it but I want to use as an array:
char ch;
FILE *file;
file = fopen("input.txt","r");
int allocated_size = 10;
int used_size = 0;
char c, *input, *tmp_input;
// allocate our buffer
input = (char*)malloc(allocated_size);
if (input == NULL) {
printf("Memory allocation error");
return 1;
}
while ((c = fgetc(file)) != EOF){
// make sure there's an empty one at the end to avoid
// having to do this check after the loop
if (used_size == allocated_size-1) {
allocated_size *= 2;
tmp_input = (char*)realloc(input, allocated_size);
if (tmp_input == NULL) {
free (input);
printf("Memory allocation error");
return 1;
}
input = tmp_input;
}
input[used_size++] = c;
}
// we are sure that there's a spot for last one
// because of if (used_size == allocated_size-1)
input[used_size] = '\0';
printf("\nEntered string in the file: %s\n", input);
But how can I use "input" like an array:
char *input[] = {"This is string value from file!", NULL};
For this case I can get access to the text in this way: input[0]
So in order to achieve this
char *input[] = {"This is string value from file!", NULL};
If I am understanding correctly from your write-up then declare input as this
char *input[2];
And every time you perform any operation on your string pointer e.g. malloc and re-alloc etc. use input[0] . This way array's first record will contain your text.
The reason behind this, the string in first record means you need array of char pointers.

Resources