I have following struct
typedef struct
{
char* city;
int temp;
} Place;`
I am attempting to read in two values from a line into an array of structs.
The lines look like:
Los Angeles; 88
I am attempting to read data into the array. Assuming my memory allocation is correct what is the correct way to read in these values.
my code
void readData(FILE** fpData, FILE** fpOutput)
{
char s[100];
int index = 0;
Place *values;
values=malloc(size * sizeof(Place));
if (values == NULL)
{
MEM_ERROR;
exit(1);
}
for (int a = 0; a < size; a++)
{
(values[a]).city = (char *) malloc(100 * sizeof(char));
if(values[a].city == NULL)
{
MEM_ERROR;
exit(100);
}
}
while(fgets(s, sizeof(s), *fpData)!=NULL)
{
sscanf(s, "%[^:]%*c%d\n", values[index].city, &values[index].temp);
index++;
}
sortInsertion(values, size, fpOutput);
free(values);
return;
}
The city is not going into the array so I am assuming the part where it says values[index].city is incorrect.
How can I fix this ?
Your data using semicolon ; while your sscanf format using colon :, make sure this is same character. If your data really use semicolon, change %[^:] part in the sscanf format to %[^;]
Here my code and how I run it to show you that it works:
#include <stdio.h>
struct Place {
char city[100];
int temp;
} values[30];
int main() {
char s[100];
int i=0, n=0;
while ( fgets(s, sizeof(s), stdin) != NULL ) {
sscanf(s, "%[^;]%*c%d\n", values[n].city, &values[n].temp);
n++;
}
printf("n=%d\n", n);
for ( i=0; i<n; i++ ) {
printf("values[%d] = (%s, %d)\n", i, values[i].city, values[i].temp);
}
}
This is how I run it on Linux:
% for a in `seq 1 3`; do echo "City-$a; $a$a"; done | ./a.out
n=3
values[0] = (City-1, 11)
values[1] = (City-2, 22)
values[2] = (City-3, 33)
sscanf(s, "%[^:]%*c%d\n", values[index].city, &values[index].temp);
This will copy everything from the start of the line read up to the first colon (:) into the city array you allocated. Your example input would seem to have a semi-colon (;), so you'll get the entire line in the city array and nothing in the temp field.
You don't do any input checking, so any too-long input line will get split into multiple (probably corrupted) cities. You'll also have problems if there are not exactly size lines in the file, as you don't check to make sure index doesn't go past size while reading, and you assume you have size entries at the end without checking.
Related
I have this code to scan the strings from a given .txt file into an array:
void readSentences(char inputFilePath[], char inputSentences[][MAX_CHAR]) {
const char *INPUT_FILE_PATH = inputFilePath;
FILE *fp = fopen(INPUT_FILE_PATH, "r+");
int lineNum = 0;
while ( fgets( inputSentences[lineNum], sizeof(inputSentences[lineNum]), fp ) != NULL ) {
lineNum++;
}
fclose (fp);
}
The input file path is just a .txt file containing the names of countries, but when I try to print the array an extra line of nonsense is printed as below:
Hong Kong
Ecuador
Dominican Republic
Taiwan
Hong Kong
Taiwan
Australia
Hong Kong
Italy
Hong Kong
Ukraine
Republic of Irelandp7Þ♣'☻§Ò♣'☻P☺þ♣'☻
Code that prints the array from main
int main() {
char array1[MAX_LINES][MAX_CHAR];
char array2[MAX_LINES][MAX_CHAR];
readSentences("..//inputFiles/input1.txt", array1);
readSentences("..//inputFiles/input2.txt", array2);
for (int i = 0; i < MAX_LINES; i++) {
printf("%s", array2[i]);
}
The arrays array1 and array2 have not been initialized before they are passed to readSentences(). If readSentences reads less than MAX_LINES files, the unread rows of the array will still contain uninitialized junk. printf("%s", array2[i]); is printing that junk, leading to undefined behavior.
There are various ways to initialize the state of array1 and array2 to something sensible before passing them to readSentences().
Initialize the array contents in the declaration:
char array1[MAX_LINES][MAX_CHARS] = {0};
char array2[MAX_LINES][MAX_CHARS] = {0};
Clear the array contents with memset():
memset(array1, 0, sizeof array1);
memset(array2, 0, sizeof array2);
Set the array rows to empty strings with strcpy():
for (int i = 0; i < MAX_LINES; i++) {
strcpy(array1[i], "");
strcpy(array2[i], "");
}
Set the array rows to empty strings by assigning '\0' to the first element of each row:
for (int i = 0; i < MAX_LINES; i++) {
array1[i][0] = '\0';
array2[i][0] = '\0';
}
It would be better for readSentences() to return the number of lines:
char inputFilePath[], char inputSentences[][MAX_CHAR]) {
...
return numLines;
}
Then the loop in main() can print just the valid lines:
nlines2 = readSentences("..//inputFiles/input2.txt", array2);
for (int i = 0; i < nlines2; i++) {
printf("%s", array2[i]);
}
This has nothing to do with the title of the question, which is about substituting '\n' with '\0', but that was a red herring. If you want to do that anyway, the usual trick is to use strcspn to find the index of the '\n' character if it is present, or the index of the end of the string if not. Either way, the character at that index can be changed to '\0':
while ( fgets( inputSentences[lineNum], sizeof(inputSentences[lineNum]), fp ) != NULL ) {
char *s = inputSentences[lineNum]; /* saves typing :-) */
s[strcspn(s, "\n")] = '\0';
lineNum++;
}
You need to modify readSentences and return the actual number of lines read from the file, either via a return value, or via a pointer parameter and print only the number of lines actually read.
Not knowing how many lines have been read makes the reading completely usless. Whatever code needs to process the lines read for whatever purpuse must know the number of lines read.
Something like this:
void readSentences(char inputFilePath[], char inputSentences[][MAX_CHAR], int *nblines) {
const char* INPUT_FILE_PATH = inputFilePath;
FILE* fp = fopen(INPUT_FILE_PATH, "r+");
int lineNum = 0;
while (fgets(inputSentences[lineNum], sizeof(inputSentences[lineNum]), fp) != NULL) {
lineNum++;
}
fclose(fp);
*nblines = lineNum;
}
int main() {
char array1[MAX_LINES][MAX_CHAR];
int nblines1;
char array2[MAX_LINES][MAX_CHAR];
int nblines2;
readSentences("..//inputFiles/input1.txt", array1, &nblines1);
readSentences("..//inputFiles/input2.txt", array2, &nblines2);
for (int i = 0; i < nblines2; i++) {
printf("%s", array2[i]);
}
}
There is still room for further improvment:
you need to check if fopen fails and act accordingly.
if the number of liens in a file is greater than MAX_LINES, you're out of luck and your array index will go out of bounds and you'll get undefined behaviour.
the // in "..//inputFiles/.... should probably be /.
I am making a language translator, and want to read from the buffer word by word and store them in a key-value struct.
The buffer contains such a file:
hola:hello
que:what
and so on. I already tried everything and I keep errors such as segmentation fault: 11 or just reading the same line again and again.
struct key_value{
char *key;
char *value;
};
...
struct key_value *kv = malloc(sizeof(struct key_value) * count);
char k[20]; //key
char v[20]; //value
int x = 0;
for(i = 0; i < numbytes; i++){
sscanf(buffer,"%21[^:]:%21[^\n]\n",k,v);
(kv + i)->key = k;
(kv + i)->value = v;
}
for(i = 0; i < count; i++){
printf("key: %s, value: %s\n",(kv + i)->key,(kv + i)->value);
}
free(buffer);
free(kv);
I expect the output to be key: hola, value: hello key: que, value: what,
but the actual output is just key: hola, value: hello again and again.
Which is the right way to do it?
There are multiple problems with your code, among them
On each loop iteration, you read from the beginning of the buffer. It is natural, then, that each iteration extracts the same key and value.
More generally, your read loop iteration variable seems to have no relationship with the data read. It appears to be a per-byte iteration, but you seem to want a per-line iteration. You might want to look into scanf's %n directive to help you track progress through the buffer.
You are scanning each key / value pair into the same local k and v variables, then you are assigning pointers to those variables to your structures. The resulting pointers are all the same, and they will become invalid when the function returns. I suggest giving structkey_value` arrays for its members instead of pointers, and copying the data into them.
Your sscanf format reads up to 21 characters each for key and value, but the provided destination arrays are not long enough for that. You need them to be dimensioned for at least 22 characters to hold 21 plus a string terminator.
Your sscanf() format and usage do not support recognition of malformed input, especially overlength keys or values. You need to check the return value, and you probably need to match the trailing newline with a %c field (the literal newline in the format does not mean what you think it means).
Tokenizing (the whole buffer) with strtok_r or strtok or even strchr instead of sscanf() might be easier for you.
Also, style note: your expressions of the form (kv + i)->key are valid, but it would be more idiomatic to write kv[i].key.
I've written a simple piece of code that may help you to solve your problem. I've used the function fgets to read from a file named "file.txt" and the function strchr to individuate the 1st occurence of the separator ':'.
Here the code:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define MAX_LINE_SIZE 256
#define MAX_DECODED_LINE 1024
struct decod {
char key[MAX_LINE_SIZE];
char value[MAX_DECODED_LINE];
};
static struct decod decod[1024];
int main(void)
{
FILE * fptr = NULL;
char fbuf[MAX_LINE_SIZE];
char * value;
int cnt=0,i;
if ( !(fptr=fopen("file.txt","r")) )
{
perror("");
return errno;
}
while( fgets(fbuf,MAX_LINE_SIZE,fptr)) {
// Eliminate UNIX/DOS line terminator
value=strrchr(fbuf,'\n');
if (value) *value=0;
value=strrchr(fbuf,'\r');
if (value) *value=0;
//Find first occurrence of the separator ':'
value=strchr(fbuf,':');
if (value) {
// Truncates fbuf string to first word
// and (++) points second word
*value++=0;
}
if (cnt<MAX_DECODED_LINE) {
strcpy(decod[cnt].key,fbuf);
if (value!=NULL) {
strcpy(decod[cnt].value,value);
} else {
decod[cnt].value[0]=0;
}
cnt++;
} else {
fprintf(stderr,
"Cannot read more than %d lines\n", MAX_DECODED_LINE);
break;
}
}
if (fptr)
fclose(fptr);
for(i=0;i<cnt;i++) {
printf("key:%s\tvalue:%s\n",decod[i].key,decod[i].value);
}
return 0;
}
This code reads all the lines (max 1024) that the file named file.txt contains, loads all individuated couples (max 1024) into the struct array decod and then printouts the content of the structure.
I wrote this code, I think it does the job! this is simpler than the accepted answer I think! and it uses just as much as memory is needed, no more.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct key_value{
char key[22];
char value[22];
};
void parse_str(char* str, struct key_value** kv_arr, int* num){
int n = 0;
int read = -1;
char k[22];
char v[22];
int current_pos = 0;
int consumed = 0;
/*counting number of key-value pairs*/
while (1){
if(current_pos > strlen(str)){
break;
}
read = sscanf(str + current_pos, "%21[^:]:%21[^\n]\n%n", k, v, &consumed);
current_pos += consumed;
if(read == 2){
++n;
}
}
printf("n = %d\n", n);
*kv_arr = malloc(sizeof(struct key_value) * n);
/*filling key_value array*/
int i = 0;
read = -1;
current_pos = 0;
consumed = 0;
while (1){
if(current_pos > strlen(str)){
break;
}
read = sscanf(str + current_pos, "%21[^:]:%21[^\n]\n%n", k, v, &consumed);
current_pos += consumed;
if(read == 2){
struct key_value* kv = &((*kv_arr)[i]);
strncpy(kv->key, k, 22);
strncpy(kv->value, v, 22);
++i;
}
}
*num = n;
}
int main(){
char* str = "hola:hello\n"
"que:what\n";
int n;
struct key_value* kv_arr;
parse_str(str, &kv_arr, &n);
for (int i = 0; i < n; ++i) {
printf("%s <---> %s\n", kv_arr[i].key, kv_arr[i].value);
}
free(kv_arr);
return 0;
}
output :
n = 2
hola <---> hello
que <---> what
Process finished with exit code 0
Note: sscanf operates on a const char*, not an input stream from a file, so it will NOT store any information about what it has consumed.
solution : I used %n in the format string to get the number of characters that it has consumed so far (C89 standard).
I need to parse a string in C by removing all non-alphabetic characters from it. To do this I am checking the ascii value of every char and making sure its within the correct bounds. It works just the way I want it to, so that's not the problem. What I am having trouble with, however, is storing the resulting strings after the parse is completed. (I am 3 weeks into C by the way) Also if you notice that I used weird sizes for the arrays, that's because I purposely made them bigger than they needed to be.
char * carry[2]; // This is to simulate argv
carry[1] = "hello1whats2up1"; // 0 is title so I placed at 1
char array[strlen(carry[1])]; // char array of string length
strcpy(array, carry[1]); // copied string to char array
char temp[strlen(carry[1]) + 1]; // Reusable char array
char * finalAnswer[10];
int m = 0, x = 0; // Indexes
if ((sizeof(carry))/8 > 1) { // We were given arguments
printf("Array: %lu\n\n", sizeof(array));
for (int i = 0; i < sizeof(array); i++)
{
if(isalpha(array[i])) { // A-Z & a-z
//printf("%s\n", temp);
temp[x] = array[i]; // Placing chars in temp array
x++;
}
else {
printf("String Length: %lu \nString Name: %s \nWord Index: %d \n\n",
strlen(temp), temp, m); // Testing Purposes
strcpy(finalAnswer[m], temp); // Copies temp into the final answer *** Source of Error
for(int w = 0; w < sizeof(temp); w++) { temp[w] = '\0'; } // Clears temp
x = 0;
m++;
}
}
printf("String Length: %lu \nString Name: %s \nWord Index: %d \n",
strlen(temp), temp, m); // Testing Purposes
strcpy(finalAnswer[m], temp);
for(int w = 0; w < sizeof(temp); w++) { temp[w] = '\0'; } // Clears temp
x = 0;
}
else { printf("No Arguments Given\n"); }
printf("\n");
** Edit
The error I keep getting is when I try copying temp to finalAnswer
** Edit 2
I solved the problem I was having with char * finalAnswer[10]
When I was trying to use strcpy on finalAnswer, I never specified the size that was needed to store the particular string. Works fine after I did it.
Since you have solved the actual string parsing, your last comment, I shall take as the actual requirement.
"... I want to create a list of words with varying length that can be accessed by index ..."
That is certainly not a task to be solved easily if one is "three weeks into C". Data structure that represents that is what main() second argument is:
// array (of unknown size)
// of pointers to char
char * argv[] ;
This can be written as an pointer to pointer:
// same data structure as char * []
char ** list_of_words ;
And this is pushing you straight into the deep waters of C. An non trivial C data structure. As a such it might require a bit more than four weeks of C.
But we can be creative. There is "inbuilt in C" one non trivial data structure we might use. A file.
We can write the words into the file. One word one line. And that is our output: list of words, separated by new line character, stored in a file.
We can even imagine and write a function that will read the word from that result "by index". As you (it seems) need.
// hint: there is a FILE * behind
int words_count = result_size () ;
const char * word = result_get_word(3) ;
Now, I have boldly gone ahead and have written "all" of it, beside that last "crucial" part. After all, I am sure you would like to contribute too.
So the working code (minus the result_size) and result_get_word() ) is alive and kicking here: https://wandbox.org/permlink/uLpAplNl6A3fgVGw
To avoid the "Wrath of Khan" I have also pasted it here:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/*
task: remove all non alpha chars from a given string, store the result
*/
int process_and_save (FILE *, const char *) ;
int dump_result(FILE *) ;
int main( const int argc, const char * argv[] )
{
const char * filename = "words.txt";
const char * to_parse = "0abra123ka456dabra789" ;
(void)(&argc) ; (void)argv ; // pacify the compiler warnings
printf("\nInput: %s", to_parse ) ;
int retval = process_and_save(fopen(filename, "w"), to_parse ) ;
if ( EXIT_FAILURE != retval )
{
printf("\n\nOutput:\n") ;
retval = dump_result(fopen(filename, "r"));
}
return retval ;
}
int process_and_save (FILE * fp, const char * input )
{
if(!fp) {
perror("File opening failed");
return EXIT_FAILURE;
}
//
char * walker = (char *)(input) ;
while ( walker++ )
{
if ( ! *walker ) break ;
if ( isalpha(*walker) ) {
fprintf( fp, "%c", *walker ) ;
// I am alpha but next one is not
// so write word end, next
if ( ! isalpha(*(walker +1) ) )
fprintf( fp, "\n" ) ;
}
}
fclose(fp);
return EXIT_SUCCESS;
}
int dump_result(FILE* fp )
{
if(!fp) {
perror("\nFile opening failed");
return EXIT_FAILURE;
}
int c; while ((c = fgetc(fp)) != EOF) { putchar(c); }
if (ferror(fp))
puts("\nI/O error when reading");
fclose(fp);
return EXIT_SUCCESS;
}
I think this is functional and does the job of parsing and storing the result. Not in the complex data structure but in the simple file. The rest should be easy. If need help please do let me know.
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;
}
Let's say I've got the file
5f2
3f6
2f1
And the code:(The printf should print the second numbers (i.e 2,6, and 1) but it doesn't
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main (int argc, char * argv[])
{
FILE *ptr;
char str[100];
char * token;
int a, b, i;
int arr[4];
if(argc > 1)
{
ptr = fopen(argv[1],"r");
if(ptr == NULL)
{
exit(1);
}
}
else
{
exit(1);
}
//And I'm looking to parse the numbers between the "f" so..
while(fgets(str,100,ptr) != NULL)
{
token = strstr(str,"f");
if(token != NULL)
{
a = atol(str); // first number
b = atol(token+1); // second number
arr[i] = b; // store each b value (3 of em) into this array
}
i++;
printf("Values are %d\n",arr[i]); //should print 2,6 and 1
}
}
I've tried to move the printf outside the loop, but that seems to print an even weirder result, I've seen posts about storing integers from a file into an array before, however since this involves using strstr, I'm not exactly sure the procedure is the same.
int i,j=0;
while(fgets(str,sizeof(str),file) != NULL)
{
size_t n = strlen(str);
if(n>0 && str[n-1] == '\n')
str[n-1] = '\0';
i = str[strlen(str)-1] - '0'; /* Convert the character to int */
printf("%d\n",i);// Or save it to your int array arr[j++] = i;
}
Just move to the last character as shown and print it out as integer.
PS: fgets() comes with a newline character you need to suppress it as shown
You are never initializing i, then you are reading into arr[i] (which just happens to not crash right there), then increment i (to "undefined value + 1"), then print arr[i] -- i.e., you are writing to and reading from uninitialized memory.
Besides, your FILE * is ptr, not file. And you should get into the habit of using strtol() instead of atol(), because the former allows you to properly check for success (and recover from error).