C - printf string with array of structures - c

I am new with programming, after searching and searching, i got some code to work (partially), i can create a struct but i can't print string fields :/
#include <stdio.h>
#define MAX 100
struct vehiculos{
float peso;
int hora;
char *id;
int ferry;
};
void asignar(struct vehiculos llegada[MAX] ...) { // Here i have more parameters, but they are useless in this question...
int id,i;
i=0;
while(!feof(input_file)){
fscanf(input_file,"%f %d %s %d",&llegada[i].peso,&llegada[i].hora,id,&llegada[i].ferry);
llegada[i].id = id;
i++;
}
}
int main(){
struct vehiculos llegada[MAX];
FILE *entrada;
entrada = fopen("proy1.txt","r");
asignar(llegada...);
return 0;
}
My problem is that everything works fine in that "asignar" function, but if i try to print the vehicle's id outside that function, it just print garbage values, however other values like peso, hora and ferry are printed correctly (outside the function)
This works:
void asignar(...){
...
printf("%s", llegada[i].id);
...
}
This doesn't work:
int main(){
...
printf("%s", llegada[i].id);
...
}
Also my compiler says that there are no errors in the code, so i don't know what is the problem here
Can anyone help me? it would be great, Thanks :)

In the best case you may get some random garbage. However, most likely your program will just segfault: Your codes declares id, but doesn't allocate space for it.
You need to declare it as
char id[IDLEN]
But then, the id you get when reading the file is a char* and the compiler will complain when trying to assign it to llegada[i].id.
For this to work, you need to make a string copy using the string.h library.
The following code works.
#include <stdio.h>
#include <string.h>
#define MAX 100
#define IDLEN 80
struct vehiculos{
float peso;
int hora;
char id[IDLEN];
int ferry;
};
void asignar(struct vehiculos llegada[MAX], FILE* input_file) { // Here i have more parameters, but they are useless in this question...
char id[IDLEN];
int i;
i=0;
while(!feof(input_file)){
fscanf(input_file,"%f %d %s %d",&llegada[i].peso,&llegada[i].hora,id,&llegada[i].ferry);
strcpy(llegada[i].id , id);
i++;
}
}
int main(){
struct vehiculos llegada[MAX];
FILE *entrada;
entrada = fopen("proy1.txt","r");
asignar(llegada,entrada);
printf("%s\n",llegada[0].id);
return 0;
}

First, you declared id as an int instead of char*.
Now, when declaring a pointer, some compilers assign null to it, and that is your case.
When you do this: llegada[i].id = id; it dosen't actually assign the string to the pointer, instead you should allocate space (malloc or similar) and use strcpy.
After creating your struct with null pointer, you are trying to print it. All "flat" members such as int will be printed just fine, but your string, as it is a pointer will print nothing, so printf("%s", llegada[i].id); won't do a thing

Rather than statically declaring id, you can also allocate space for it as needed. Below is an example that shows the use of fgets and sscanf to handle reading and parsing each line read. It has several advantages over fscanf (most notably here allowing you to read and allocate id as needed). Let me know if you have questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
#define MAXL 256
struct vehiculos {
float peso;
int hora;
char *id;
int ferry;
};
void asignar (struct vehiculos *llegada, FILE *fp, int *idx)
{ // Here i have more parameters, but they are useless in this question...
char line[MAXL] = {0};
char idbuf[MAXL] = {0};
while (fgets (line, MAXL, fp)) {
sscanf (line, "%f %d %s %d", &llegada[*idx].peso, &llegada[*idx].hora,
idbuf, &llegada[*idx].ferry);
llegada[*idx].id = strdup (idbuf);
(*idx)++;
}
// while (!feof (input_file)) {
// fscanf (input_file, "%f %d %s %d", &llegada[i].peso, &llegada[i].hora,
// id, &llegada[i].ferry);
// llegada[i].id = id;
// i++;
// }
}
int main (int argc, char **argv)
{
FILE *entrada = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!entrada) {
fprintf (stderr, "error: failed to open input for reading.\n");
return 1;
}
int i, n = 0;
struct vehiculos llegada[MAX] = {{0,0,NULL,0}};
asignar (llegada, entrada, &n);
if (entrada != stdin) fclose (entrada);
for (i = 0; i < n; i++)
printf (" llegada[%2d] peso: %3.2f hora: %2d id: %8s ferry: %d\n",
i, llegada[i].peso, llegada[i].hora, llegada[i].id, llegada[i].ferry);
for (i = 0; i < n; i++)
free (llegada[i].id);
return 0;
}
Sample Input
$ cat dat/vehiculos.txt
6.80 4 sal_135 2
8.20 3 jim_023 4
5.45 5 sam_101 6
6.75 3 bil_002 16
Example Output
$ ./bin/struct_no_alloc dat/vehiculos.txt
llegada[ 0] peso: 6.80 hora: 4 id: sal_135 ferry: 2
llegada[ 1] peso: 8.20 hora: 3 id: jim_023 ferry: 4
llegada[ 2] peso: 5.45 hora: 5 id: sam_101 ferry: 6
llegada[ 3] peso: 6.75 hora: 3 id: bil_002 ferry: 16

Related

How do you assign structs into an array?

I have currently made this much of the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#define STRSIZE 21
struct PInven{
int count;
struct PItem{
char name[STRSIZE];
int amount;
}Pitem;
}Pinven;//this needs to be an output file
int ReadInProduce (){
//read in file and check to see if the file exist or not.
FILE * PinFile = fopen("produce.txt","r");
if (PinFile == NULL){
printf("ERROR: WRONG FILE");
}
else{
printf("I did it!!\n");
}
//assigning the value gotten into the struct variable(but need to maybe change this since it needs to be an output)
fscanf(PinFile,"%d",&Pinven.count);
printf("%d\n", Pinven.count);
int i;
for(i =0; i <Pinven.count; i++){
fscanf(PinFile,"%20s %d",Pinven.Pitem.name, &Pinven.Pitem.amount);
printf("%s %d\n",Pinven.Pitem.name, Pinven.Pitem.amount);
}
//making an array to hold the variables
//FILE * PoutFile = fopen("produce_update.txt","w");
fclose(PinFile);
return 0;
}
From there I want to get the file that is read to the structs to be printed out into an array so that later on I can make a function that will be able to compare to the to it.
Basically a store management system. Where the file of the inventory is read in and compared to the file that is store and return a new value for the amount of produce now either left or gained.
10 //number of items that will be stored in the store
apple 19
banana 31
broccoli 9
...
In general, it's a really bad idea to include header information in the file about the number of entries in the file. You want to be able to do stream processing, and that will be more difficult if you need that meta-data. More importantly, it is important to understand how to write the code so that you don't need it. It's not really that difficult, but for some reason people avoid it. One simple approach is just to grow the array for each entry. This is horribly inefficient, but for the sake of simplicity, here's an example that expects the file not not include that first line:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include <limits.h>
#define STRSIZE 128
struct PItem{
char name[STRSIZE];
int amount;
};
struct PInven{
int count;
struct PItem *PItem;
};
static void
grow(struct PInven *p)
{
p->PItem = realloc(p->PItem, ++p->count * sizeof *p->PItem);
if( p->PItem == NULL ){
perror("out of memory");
exit(1);
}
}
int
ReadInProduce(struct PInven *P, const char *path)
{
FILE * PinFile = fopen(path, "r");
if( PinFile == NULL ){
perror(path);
exit(1);
}
char fmt[64];
int max_len;
max_len = snprintf(fmt, 0, "%d", INT_MAX);
snprintf(fmt, sizeof fmt, "%%%ds %%%dd", STRSIZE - 1, max_len - 1);
grow(P);
struct PItem *i = P->PItem;
while( fscanf(PinFile, fmt, i->name, &i->amount) == 2 ){
i += 1;
grow(P);
}
P->count -= 1;
fclose(PinFile); /* Should check for error here! */
return P->count;
}
int
main(int argc, char **argv)
{
struct PInven P = {0};
char *input = argc > 1 ? argv[1] : "produce.txt";
ReadInProduce(&P, input);
struct PItem *t = P.PItem;
for( int i = 0; i < P.count; i++, t++ ){
printf("%10d: %s\n", t->amount, t->name);
}
}
As an exercise for the reader, you should add some error handling. At the moment, this code simply stops reading the input file if there is bad input. Also, it would be a useful exercise to do fewer reallocations.
you should change Structure of PInven to it can save a dynamic array of Pitem with a Pitem pointer.
tested :
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#define STRSIZE 21
typedef struct {
char name[STRSIZE];
int amount;
} Pitem;
struct PInven {
int count;
Pitem *pitem;
} Pinven; // this needs to be an output file
int main() {
// read in file and check to see if the file exist or not.
FILE *PinFile = fopen("produce.txt", "r");
if (PinFile == NULL) {
printf("ERROR: WRONG FILE");
} else {
printf("I did it!!\n");
}
// assigning the value gotten into the struct variable(but need to maybe
// change this since it needs to be an output)
fscanf(PinFile, "%d", &Pinven.count);
Pinven.pitem = (Pitem *)malloc(sizeof(Pitem) * Pinven.count);
printf("%d\n", Pinven.count);
int i;
for (i = 0; i < Pinven.count; i++) {
fscanf(PinFile, "%20s %d", Pinven.pitem[i].name,
&Pinven.pitem[i].amount);
// printf("%s %d\n",Pinven.pitem[i].name, Pinven.pitem[i].amount);
}
for (i = 0; i < Pinven.count; i++) {
printf("%s %d\n", Pinven.pitem[i].name, Pinven.pitem[i].amount);
}
// making an array to hold the variables
// FILE * PoutFile = fopen("produce_update.txt","w");
fclose(PinFile);
// remember free
free(Pinven.pitem);
return 0;
}

how to dynamically populate a string array in c and why by removing unnecessary code the program doesn't work?

i'm trying to populate an array of strings in c using dynamic allocation, this is the code snippet
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct test_struct {
char **names;
int age;
} test_struct;
void add_names(char*** array, int number_of_names);
int main() {
test_struct* ts;
char **array;
int number_of_names;
ts = (test_struct*)malloc(sizeof(test_struct));
printf("input the number of names to add: ");
scanf("%d", &number_of_names);
add_names(&array, number_of_names);
printf("%s | %s\n", array[1], array[0]);
return 0;
}
void add_names(char*** array, int number_of_names) {
char *string;
*array = malloc(sizeof(char*)*number_of_names);
for (int i = 0; i < number_of_names; i++){
scanf("%ms", &string);
*array[i] = malloc(sizeof(char)*strlen(string) + 1);
strcpy(*array[i], string);
}
printf("%s | %s\n", *array[0], *array[1]);
}
If I try to populate it with three names I can't get over the second, and if I try to do it with two names the printf statement inside the function prints both names, however the one in the main function prints ( [name1] | (null) )
.
Also if I remove the unnecessary struct part, I have this code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void add_names(char*** array, int number_of_names);
int main() {
char **array;
int number_of_names;
printf("input the number of names to add: ");
scanf("%d", &number_of_names);
add_names(&array, number_of_names);
printf("%s | %s\n", array[1], array[0]);
return 0;
}
void add_names(char*** array, int number_of_names) {
char *string;
*array = malloc(sizeof(char*)*number_of_names);
for (int i = 0; i < number_of_names; i++) {
scanf("%ms", &string);
*array[i] = malloc(sizeof(char)*strlen(string) + 1);
strcpy(*array[i], string);
}
printf("%s | %s\n", *array[0], *array[1]);
}
That doesn't even work with 2 as input. What am I doing wrong here?

returning 2d array in C…

I'm a total noob in C. I can't make the connect between this function and main. I'm trying to print out a 2d array and I keep getting segmentation fault. Any help would be greatly appreciated.
EDIT: When I changed the last line 'printf("%d:[%s]\n",i,*(p+i))' from %s to %c, I get the first word in the file i'm reading from. So turns out that something is in fact being returned from my function. Now just need to figure out how to get it to return words from other lines in the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define num_strings 20
#define size_strings 20
int *read_file(){
int j = 0;
static char text[num_strings][size_strings];
FILE *fp;
int x;
fp = fopen("dictionary2.txt", "r");
char s[100];
while(!feof(fp)) {
x = fscanf(fp,"%[^\n]",s);
fgetc(fp);
if (x==1) {
strcpy(text[j],s);
j++;
}
}
return text;
}
int main() {
int *p;
p = read_file();
int i;
for(i = 0; i < 10; i++) {
printf("%d:[%s]\n",i,*(p+i));
}
return(0);
}
In general, you should be creating your array in main() and passing it in, this kind of behavior is very unorthodox. However, if you do insist on doing it this way, you have to return a pointer to your array, since you cannot return arrays in C.
This is the kind of thing you'll need:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define num_strings 20
#define size_strings 20
typedef char (*PARR)[num_strings][size_strings];
PARR read_file(int * wordsread)
{
static char text[num_strings][size_strings];
FILE *fp;
if ( (fp = fopen("dictionary2.txt", "r")) == NULL ) {
fprintf(stderr, "Couldn't open file for reading\n");
exit(EXIT_FAILURE);
}
char s[100];
int j = 0;
while ( j < num_strings && fgets(s, sizeof s, fp) ) {
const size_t sl = strlen(s);
if ( s[sl - 1] == '\n' ) {
s[sl - 1] = 0;
}
if ( (strlen(s) + 1) > size_strings ) {
fprintf(stderr, "String [%s] too long!\n", s);
exit(EXIT_FAILURE);
}
strcpy(text[j++], s);
}
fclose(fp);
*wordsread = j;
return &text;
}
int main(void)
{
int wordsread = 0;
PARR p = read_file(&wordsread);
for ( int i = 0; i < wordsread; ++i ) {
printf("%d:[%s]\n", i, (*p)[i]);
}
return 0;
}
which, with a suitable input file, outputs:
paul#horus:~/src/sandbox$ ./twoarr
0:[these]
1:[are]
2:[some]
3:[words]
4:[and]
5:[here]
6:[are]
7:[some]
8:[more]
9:[the]
10:[total]
11:[number]
12:[of]
13:[words]
14:[in]
15:[this]
16:[file]
17:[is]
18:[twenty]
19:[s'right]
paul#horus:~/src/sandbox$
Note this only works because you declared your array in read_file() as static - don't return pointers to local variables with automatic storage duration in this way.
Try moving your #defines back and changing your function header to return a pointer to arrays of size_strings characters, as follows:
#define num_strings 20
#define size_strings 20
char (*read_file())[size_strings] {
Or alternately, with a typedef:
#define num_strings 20
#define size_strings 20
typedef char (*PCharArr)[size_strings];
PCharArr read_file() {
...and change the type of p in main accordingly:
char (*p)[size_strings];
That will return (a pointer to the first element of) an array of character arrays, which is more or less equivalent to a 2D array of char.
Update, oh I see, you pasted the code from main to the function, I know what happened here, you assumed p[20][20] is the same as a p* or maybe a p**, that's not correct, since now if you do *(p+1), the compiler doesn't know each element in p is 20 wide instead of 1 wide. You approach here should be to declare a pointer to an array of strings in read_file and return that instead:
static char text[num_strings][size_strings];
static char *texts[num_strings]
...
while....
....
if (x==1)
{strcpy(text[j],s);texts[j]=text[j];j++;}
return texts;
your p should be char* not int*. You also need to terminate the loop if 20 items have been read in.

Read file .txt and save it to struct in c

I want to ask about file processing and struct in C language, I get an assignment from my lecture, and am so really confused about string manipulation in C programming. Here is the task.
get data from mhs.txt
store in struct
sort by name ascending
Here is the mhs.txt
1701289436#ANDI#1982
1701317124#WILSON#1972
1701331734#CHRISTOPHER STANLEY#1963
1701331652#SHINVANNI THEODORE#1962
1701331141#MUHAMMAD IMDAAD ZAKARIA#1953
1701331564#MARCELLO GENESIS DRIET J.#1942
1701322282#ANANDA AULIA#1972
1701329175#LORIS TUJIBA SOEJONOPOETRO#1983
1701301422#DEWI JULITA#1993
1701332610#HARRY HUTALIANG#1982
first before # is NIM,
after first # is name
and the last after #, is year
and here is what i've done
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student{
char nim[11];
char name[50];
int year;
}s[10];
int main(){
FILE *fp;
int c,i,n;
printf("Read mhs.txt...");
getchar();
fp = fopen("mhs.txt", "r");
c = getc(fp);
i = 0;
while(c!=EOF){
printf("%c", c);
c = getc(fp);
i++;
}
fclose(fp);
getchar();
return 0;
}
First thing, I could save data on struct, but in here I very confused to separate a string.
That's all I know about struct and file processing, is there anyone who can help me? I have traveled around the internet and could not find the correct results.
Sorry if there are duplicate questions, and sorry if my english is too bad.
This is pure C code, you should new three import function: strtok & qsort & fsan.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student
{
char nim[11];
char name[50];
int year;
};
#define BUFFER_SIZE 100
struct student saveToStruct (char* str)
{
struct student res;
int flag = 0;
char *token = strtok(str, "#");
while( token != NULL )
{
if (0 == flag)
strcpy(res.nim, token);
else if (1 == flag)
strcpy(res.name, token);
else
res.year = atoi(token);
flag++;
token = strtok( NULL, "#" );
}
return res;
}
void print(struct student* arr, int size)
{
for (int i = 0; i < size; i++)
{
printf("%s, %s, %d\n", arr[i].nim, arr[i].name, arr[i].year);
}
}
int cmp(const void* l, const void* r)
{
return strcmp(((const student*)l)->name, ((const student*)r)->name);
}
int main()
{
struct student arr[10];
FILE* file = fopen("mhs.txt", "r");
if (!file)
return -1;
char buffer[BUFFER_SIZE];
int flag = 0;
while (fgets(buffer, BUFFER_SIZE, file))
{
arr[flag] = saveToStruct(buffer);
flag++;
}
print(arr, 10);
qsort(arr, 10, sizeof(struct student), cmp);
printf("After sort by name!\n");
print(arr, 10);
return 0;
}
Since you've tagged this as C++, I'd use C++:
#include <iostream>
#include <string>
#include <algorithm>
struct student {
std::string nim;
std::string name;
int year;
bool operator<(student const &other) {
return name < other.name;
}
friend std::istream &operator>>(std::istream &is, student &s) {
std::getline(is, s.nim, '#');
std::getline(is, s.name, '#');
return is >> s.year;
}
};
int main() {
std::ifstream in("mhs.txt");
std::vector<student> students{
std::istream_iterator<student>(in),
std::istream_iterator<student>()
};
std::sort(students.begin(), students.end());
}
If you want to accomplish roughly the same thing in C, it's probably easiest to do the reading with fscanf using a scanset conversion, like:
fscanf(infile, "%10[^#]#%49[^#]#%d", student.nim, student.name, &student.year);
The scanset conversion gives you something like a subset of regular expressions, so the %[^#] converts a string of characters up to (but not including) a #. In this case, I've limited the length of each to one less than the length you gave for the arrays in your struct definition to prevent buffer overruns.
Then you can do the sorting with qsort. You'll need to write a comparison function, and doing that correctly isn't always obvious though:
int cmp(void const *aa, void const *bb) {
student const *a = aa;
student const *b = bb;
return strcmp(a->name, b->name);
}
Here are some hints, not the full answer. Hope it could help you.
first you need to read the file line by line, instead of character by character. You need the function of fgets(). you may find the reference from www.cplusplus.com/reference/cstdio/fgets/
second you can use strtok() to seperate strings. here is an example.
char str[] = "now # is the time for all # good men to come to the # aid of their country";
char delims[] = "#";
char *result = NULL;
result = strtok( str, delims );
while( result != NULL ) {
printf( "result is \"%s\"\n", result );
result = strtok( NULL, delims );
}
and you may find the reference to strtok() from http://www.cplusplus.com/reference/cstring/strtok/
third, use qsort() to sort the structure array. you may find the reference of it from http://www.cplusplus.com/reference/cstdlib/qsort/. examples can also be found there.

Segmentation error in Xcode, debugger cant help me

I have been going crazy trying to figure out what is done wrong. I admit I am inexperienced when it comes to C, but I don't know what is wrong. Is the way that I am accessing/using the struct incorrect?
EDIT: I keep getting EXC_BAD_ACCESS in debugger.
#include <stdio.h>
#include <string.h>
#define MAX_STRING 20
#define MAX_PLYR 16
typedef struct {
char pname[MAX_STRING];
int runs;
char *s;
} Team_t;
int
main(void)
{
Team_t *team_data[MAX_PLYR];
int i;
char *p;
char name[MAX_STRING];
FILE *inp;
inp = fopen("teamnames.rtf", "r");
for (i = 0; i < MAX_PLYR;) {
while ((fgets(name, MAX_STRING, inp) != NULL));
printf("Name(i): %s\n", name);
strcpy(team_data[i]->pname, name);
i++;
}
fclose(inp);
return(0);
}
Edit: Here's what's changed, still getting Segmentation Error
#include <stdio.h>
#include <string.h>
#define MAX_STRING 20
#define MAX_PLYR 16
typedef struct {
char pname[MAX_STRING];
int runs;
char s;
} Team_t;
int
main(void)
{
Team_t team_data[MAX_PLYR];
char name[MAX_STRING];
int i;
FILE *inp;
inp = fopen("teamnames.rtf", "r");
for (i = 0; i < MAX_PLYR; i++) {
((fgets(name, MAX_STRING, inp)));
if (feof(inp)) {
printf("End of stream\n");
i = MAX_PLYR;
}
else {
if (ferror(inp)) {
printf("Error reading from file\n");
}
printf("Name(i): %s\n", name);
strcpy(team_data[i].pname, name);
}
}
fclose(inp);
return(0);
}
You declare team_data but you don't allocate it; therefore it's pointing off into random memory, as are the imaginary contents of the array. You need to actually create the array, something like
Team_t *team_data[MAX_PLYR] = (Team_t**) malloc(MAX_PLYR * sizeof(Team_t *));
Use structs, not pointers (or if you insist using pointers the allocate space for those structs)
Team_t team_data[MAX_PLYR];
fgets(team_data[i].pname, MAX_STRING, inp)
when you write
Team_t *team_data[MAX_PLYR];
you are not allocating any memory for the actual Team_t records, instead you are setting up an array of pointers to records.
If instead you would write
Team_t team_data[MAX_PLYR];
you would have allocated the records. When you then want to copy into the team_data array you write instead
strcpy( team_data[i].name, name );

Resources