Hi I'm trying to figure how to count characters in comments in c program. So far i had written a function that doesn't work, but seems logical. Can you please help me complete my task.My quest is to fill buffer with all the characters from the comments and then count them.
void FileProcess3(char* FilePath)
{
char myString [1000];
char buffer[1000];
FILE* pFile;
int i = 0;
pFile = fopen (FilePath, "r");
while(fgets( myString, 1000, pFile) != NULL)
{
int jj = -1;
while(++jj < strlen(myString))
{
if ( myString[jj] == '/' && myString[jj+1] == '*')
{
check = 1;
jj++;
jj++;
}
if( check == 1 )
{
if ( myString[jj] == '*' && myString[jj+1] == '/')
{
check = 0;
break;
}
strcat( buffer, myString[jj] );
}
}
}
printf(" %s ", buffer );
fclose(pFile);
}
E.g. fix to
int i = 0, check = 0;
...
if( check == 1 )
{
if ( myString[jj] == '*' && myString[jj+1] == '/')
{
check = 0;
break;
}
buffer[i++] = myString[jj];
}
}
}
buffer[i]='\0';/* add */
strcat() concatenates (NUL-terminated) strings, so this is definitely wrong
(and should give a compiler warning due to the wrong type of the second argument):
strcat( buffer, myString[jj]);
You could do something like
buffer[length] = myString[jj];
buffer[length+1] = 0;
length++;
where length is an integer initialized to zero that keeps track of the current length.
Of course you should check the length against the available size of the buffer
to avoid a buffer(!) overflow.
If your intention is only to count the characters, then you don't have to copy
them to a separate buffer at all. Just increment a counter.
You should also note that fgets() does not remove the newline characters from the
input. So you have to check for that if you don't want to include the newlines
in the count.
Related
So I am trying to take the users input using read(). The while loop runs until ctrl+d is entered in linux terminal and the input is stopped. I wrote some code and it works, but the problem I am running into is that I don't know how to take exactly whats in the input and how to use it.
In the code below, right after if(buff[offset] == '\n') (runs the given input, and after its finished clears the buffer and moves on to the next input) I don't know how to go into the 3 possible cases of input and also I don't know how to get the number from the second case:
"e" - exit the program
"p N" - do something, N is a number which I also need to get into a variable (I think that buff[offset-1] should get the number but I am not quite sure.)
everything else - print a message
Code:
int fd = 0; // set read() to read from STDIN_FILENO, because it's number is 0
const size_t read_size = 1; // set chunk size
size_t size = read_size;
size_t offset = 0;
size_t res = 0;
char *buff = malloc(size+1);
*buff = '\0';
while((res = read(fd, buff + offset, read_size)) > 0) // read from stdin and save to buff
{
if(buff[offset] == '\n')
{ // THIS PART
buff[offset] = '\0';
if(buff == "e")
{
// exit the program
return 0;
}
else if(buff == "p")
{
// do sth
}
else
{
// print a message
}
// reset the buffer (free its memory and allocate new memory for the next input)
offset = 0;
size = read_size;
free(buff);
buff = NULL;
buff = malloc(size + 1);
*buff = '\0';
}
else
{
offset += res;
buff[offset] = '\0';
if (offset + read_size > size)
{
size *= 2;
buff = realloc(buff, size+1);
}
}
}
If this isn't possible with read() I can try with something else like fgets() perhaps?
if(buff == "p")
This line checks if the string buff is stored at the same address as the string "p". However, you don't want to check if the strings are stored at the same address, but if they have the same content.
... how would strcmp() work ...
Using strcmp() you can check if two strings are identical:
if(!strcmp(buff, "e"))
"p N" ... since the number N could be any number?
Simply check if the first character of buff is 'p':
if(buff[0] == 'p')
Note that single quotes (') are used instead of double quotes (") for character constants (in contrast to string constants).
As I described in my comment above, here is a full answer:
int main(void)
{
char line[100], cmd;
int num, cnt;
while(fgets(line,sizeof(line),stdin)) != EOF) {
cnt = sscanf(line," %c %d",&cmd,&num);
if (cnt == 0) {
// empty line, no command
} else if (cmd == 'e') {
// exit the program
break;
} else if (cmd == 'p' && cnt == 2) {
// no something with num
} else {
// print a message
}
}
return 0;
}
I am having a situation with strncmp function in C, it is returning 0 even when the words do not match, in the example below, I am testing it with the letter 'R' and when running the code it returns 0 even when the compared word in the txt document is 'RUN'. Do you happen to know whether
Am I missing something in the strncmp function or somewhere else in my code?
Thank you for your input.
bool lookup(string s);
int main(void) {
char *s;
s = "R";
if (lookup(s)) {
printf("Word found =)\n");
} else {
printf("Word not found =(\n");
}
}
// Looks up word, s, in txt document.
bool lookup(string s)
{
// TODO
char *wordtosearch;
wordtosearch = s;
int lenwordtosearch = strlen(wordtosearch);
char arraywordindic[50];
// Open txt file
FILE *file = fopen("text.txt", "r");
if (file == NULL)
{
printf("Cannot open file, please try again...\n");
return false;
}
while (!feof(file)) {
if (fgets(arraywordindic, 50, file) != NULL) {
char *wordindic;
wordindic = arraywordindic;
int result = strncmp(wordindic, wordtosearch, lenwordtosearch);
if (result == 0) {
printf("%i\n", result);
printf("%s\n", wordindic);
printf("%s\n", wordtosearch);
fclose(file);
return true;
}
}
}
fclose(file);
return false;
}
The thing is that it compares R with RUN and it gives 0. I want it to
return 0 when it finds R only.
In this case you need to compare whole words using the function strcmp instead of comparing only lenwordtosearch characters using the function strncmp.
Take into account that the function fgets can append the new line character '\n' to the entered string. You need to remove it before comparing strings.
if (fgets(arraywordindic, 50, file) != NULL) {
arraywordindic[ strcspn( arraywordindic, "\n" ) ] = '\0';
int result = strcmp(arraywordindic, wordtosearch);
if (result == 0) {
printf("%i\n", result);
printf("%s\n", arraywordindic);
printf("%s\n", wordtosearch);
As a result these declarations
int lenwordtosearch = strlen(wordtosearch);
and
char *wordindic;
wordindic = arraywordindic
may be removed.
And the condition of the while loop should be written like
while ( fgets(arraywordindic, 50, file) != NULL ) {
arraywordindic[ strcspn( arraywordindic, "\n" ) ] = '\0';
int result = strcmp(arraywordindic, wordtosearch);
if (result == 0) {
printf("%i\n", result);
printf("%s\n", arraywordindic);
printf("%s\n", wordtosearch);
//...
int result = strncmp(wordindic, wordtosearch, lenwordtosearch);
This is going to give you zero if the first lenwordtosearch characters of wordtosearch matches the first lenwordtosearch characters of any word in the dictionary.
Given that the word you're searching for is S, any word in the dictioanary that starts with S is going to give you a match.
You should probably be checking the entire word. That probably means cleaning up the word you've read in from the file (i.e., removing newline) and using strcmp() instead, something like:
wordindic = arraywordindic;
// Add this:
size_t sz = strlen(wordindic);
if (sz > 0 && wordindic[sz - 1] == '\n')
wordindic[sz - 1] = '\0';
// Modify this:
// int result = strncmp(wordindic, wordtosearch, lenwordtosearch);
int result = strcmp(wordindic, wordtosearch);
I have a file with different words separated by newlines. How can i read and store each word in an array of strings?
1 word, 1 row of the array.
I'm posting this code, but im pretty sure it doesnt work, because i can't understand if i should use fgets or fscanf and how i can write each word in each line of my array.
int file_string_temp_number_rows=200;
int file_string_temp_number_cols=200;
char **file_string_arr = (char**)malloc (file_string_temp_number_rows*sizeof(char));
for ( i = 0 ; i < file_string_temp_number_rows ; i++){
file_string_arr[i] = (char*)malloc(file_string_temp_number_cols*sizeof(char));
}
if ((file_ptr= fopen(filename, "r"))){
if((file_ptr=fopen(filename,"r"))==NULL)
{
printf("errore apertura file");
return 1;
}
else{
while(!feof(file_ptr)){
for(i = 0 ; i < file_string_temp_number_rows ; i++){
for(j = 0 ; j < file_string_temp_number ; j++){
fgets(file_string_arr , 40 , filename);
}
}
}
}
}
}
Addressing your title question: How do i read an array of strings from a file?
There are many approaches to do this. Here is a list of basic steps that could be used.
1) Using fopen(), open file and scan to determine the following:
- Maximum word length.
- Count of words in file.
2) Create container: - Use calloc() to create an array of strings for words.
3) Using fopen() (again), fgets() and strtok() (or variant) to parse content of file into string array.
Note, The sample implementation snippets below use particular functions and techniques, but you should not limit your implementation to only these. There are many paths that would work just as well, so do not be afraid to experiment. For example, either fgets() or fscanf() could be used to solve this problem. The methods highlighted below are just examples of one way to do the task.
Scan example
// provides count of words, and longest word
int longestWord(char *file, int *nWords)
{
FILE *fp=0;
int cnt=0, longest=0, numWords=0;
char c;
fp = fopen(file, "r");
if(fp)
{
// if((strlen(buf) > 0) && (buf[0] != '\t') && (buf[0] != '\n') && (buf[0] != '\0')&& (buf[0] > 0))
while ( (c = fgetc(fp) ) != EOF )
{
if ( isalnum (c) ) cnt++;
else if ( ( ispunct (c) ) || ( isspace(c) ) || (c == '\0' ))
{
(cnt > longest) ? (longest = cnt, cnt=0) : (cnt=0);
numWords++;
}
}
*nWords = numWords;
fclose(fp);
}
else return -1;
return longest;
}
//in main eg:
int longest;
int count;
...
longest = longestWord(".\\file.txt", &count);//longest and count will be
//used to create string arrays
Create string array example
//Create string arrays to contain words using result from original scan of file
char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
// in main(): Create array of words
char **words = Create2DStr(count, longest);//Using values obtained from scan section above
if(words) { //continue
Parse into word strings example
// in main(), after performing scan and array creation:
const char delim[] = {" \n\t"};
char line[260];
char *buf = NULL;
fp = fopen(".\\file.txt", "r");
cnt=0;
while(fgets(line, 260, fp))//keep reading lines until EOF
{
buf = strtok(line, delim);
while(buf)//continue until last word in line is parsed
{
if((strlen(buf) > 0)
{
strcpy(words[cnt], buf);
cnt++; //use as accurate count of words.
}
buf = strtok(NULL, DELIM);
}
}
fclose(fp);
I'm trying to read a config file and parse the config directives. So far I have the following code, I need advice on how to improve this or change it. Is this efficient? Thanks!
struct config
{
char host;
char port;
}
void parse_line(char *buf) {
char *line;
if(strstr(buf, "host=") || strstr(buf, "host = ") || strstr(buf, "host= ") || strstr(buf, "host =")) {
line = strstr(buf, "=");
printf("Host: %s", &line[2]);
} else if(strstr(buf, "port=") || strstr(buf, "port = ") || strstr(buf, "port= ") || strstr(buf, "port =")) {
line = strstr(buf, "=");
printf("Port: %s", &line[2]);
}
}
int main(int argc, char *argv[])
{
char *file_name;
FILE *file;
file_name = argv[1];
file = fopen(file_name, "r");
// check if file is NULL, etc..
char buffer[BUFSIZ];
char *line;
int i;
while(fgets(buffer, sizeof(buffer), file) != NULL) {
for(i = 0; i < strlen(buffer); i++) { // iterate through the chars in a line
if(buffer[i] == '#') { // if char is a #, stop processing chars on this line
break;
} else if(buffer[i] == ' ') { // if char is whitespace, continue until something is found
continue;
} else {
parse_line(buffer); // if char is not a # and not whitespace, it is a config directive, parse it
break;
}
}
}
fclose(file);
return 0;
}
I am looking for a way to ignore # if it is a first character on a line, and also lines that are white spaces. I think my code does that, but is that efficient?
EDIT:
Thanks everyone for all the suggestions, I have managed to do this simple code to trim the white spaces, so that I wouldn't need all the strstr() calls.
void trim(char *src)
{
int i, len;
len = strlen(src);
for(i = 0; i < len; i++) {
if(src[i] == ' ') {
continue;
}
if(src[i] == '\n' || src[i] == '#') {
break;
}
printf("%c", src[i]); // prints: host=1.2.3.4
}
}
int main(void)
{
char *str = "host = 1.2.3.4 # this is a comment\n";
trim(str);
return EXIT_SUCCESS;
}
It prints correctly: host=1.2.3.4 but now I need this in a variable to be further parsed. I think I will try to use strcpy.
EDIT 2:
I do not think that strcpy is the right choice. Those chars are printed out in a loop, so every time I use strcpy, the previous char is overwritten. I have tried this, but it does not work because only the host= part is placed into arr. The IP part is not placed into arr.. how can this be fixed..
char arr[sizeof(src)];
for(i = 0; i < len; i++) {
if(src[i] == ' ') {
continue;
}
if(src[i] == '\n' || src[i] == '#') {
break;
}
printf("%c", src[i]); // prints: host=1.2.3.4
arr[i] = src[i];
}
int j;
for(j = 0; j < sizeof(arr); j++) {
printf("%c", arr[j]); //prints: host=
}
EDIT 3:
I found the correct way of placing chars into arr:
int i, count = 0;
for(i = 0; i < len; i++) {
if(src[i] == ' ') {
continue;
}
if(src[i] == '\n' || src[i] == '#') {
break;
}
arr[count] = src[i];
count++;
}
Your implementation is pretty fragile. Parsers really ought to verify syntax and return errors when they see something unexpected. For example, yours should detect missing fields and multiply defined ones.
Fortunately this parsing problem is simple enough for sscanf to handle everything:
skip blank lines,
skip comments
ignore any amount of whitespace
extract the key/value pairs
Here's code:
#include <stdio.h>
#define CONFIG_SIZE (256)
#define HOST_SET (1)
#define PORT_SET (2)
typedef struct config {
unsigned set;
char host[CONFIG_SIZE];
unsigned long port;
} CONFIG;
// Parse the buffer for config info. Return an error code or 0 for no error.
int parse_config(char *buf, CONFIG *config) {
char dummy[CONFIG_SIZE];
if (sscanf(buf, " %s", dummy) == EOF) return 0; // blank line
if (sscanf(buf, " %[#]", dummy) == 1) return 0; // comment
if (sscanf(buf, " host = %s", config->host) == 1) {
if (config->set & HOST_SET) return HOST_SET; // error; host already set
config->set |= HOST_SET;
return 0;
}
if (sscanf(buf, " port = %lu", &config->port) == 1) {
if (config->set & PORT_SET) return PORT_SET; // error; port already set
config->set |= PORT_SET;
return 0;
}
return 3; // syntax error
}
void init_config(CONFIG *config) {
config->set = 0u;
}
void print_config(CONFIG *config) {
printf("[host=%s,port=", config->set & HOST_SET ? config->host : "<unset>");
if (config->set & PORT_SET) printf("%lu]", config->port); else printf("<unset>]");
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s CONFIG_FILE\n", argv[0]);
return 1;
}
FILE *f = fopen(argv[1], "r");
char buf[CONFIG_SIZE];
CONFIG config[1];
init_config(config);
int line_number = 0;
while (fgets(buf, sizeof buf, f)) {
++line_number;
int err = parse_config(buf, config);
if (err) fprintf(stderr, "error line %d: %d\n", line_number, err);
}
print_config(config);
return 0;
}
With this input:
# This is a comment
This isn't
# Non-leading comment
host = 123.456.789.10
###
port =42
port= 1
host=fruit.foo.bar
the output is
error line 3: 3
error line 10: 2
error line 11: 1
[host=fruit.foo.bar,port=1]
Note that when the parser discovers a field has already been set, it still uses the latest value in the config. It's easy enough to keep the original instead. I'll let you have that fun.
I think parse_line is a little bit rigid for my taste, I would use strtok
instead. Then you don't have to worry too much about spaces, like you do if you
have a space before the = sign.
Your struct is also wrong, host and port would only hold a character.
Besides port should be an integer. And you need a semicolon ; after the
struct definition.
struct config
{
char host[100];
int port;
};
int parse_line(struct config *config, char *buf)
{
if(config == NULL || buf == NULL)
return 0;
char varname[100];
char value[100];
const char* sep = "=\n"; // get also rid of newlines
char *token;
token = strtok(buf, sep);
strncpy(varname, token, sizeof varname);
varname[sizeof(varname) - 1] = 0; // making sure that varname is C-String
trim(varname);
token = strtok(NULL, sep);
if(token == NULL)
{
// line not in format var=val
return 0;
}
strncpy(value, token, sizeof value);
value[sizeof(varname) - 1] = 0
trim(value);
if(strcmp(varname, "port") == 0)
{
config->port = atoi(value);
return 1;
}
if(strcmp(varname, "host") == 0)
{
strncpy(config->host, value, siezof config->host);
config->host[(sizeof config->host) - 1] = 0;
return 1;
}
// var=val not recognized
return 0;
}
Note that I used a function called trim. This function is not part of the
standard library. Below I posted a possible implementation of such a function.
I like using trim because it gets rid of white spaces. Now you can do this in
main:
struct config config;
// initializing
config.port = 0;
config.host[0] = 0;
int linecnt = 0;
while(fgets(buffer, sizeof(buffer), file) != NULL) {
linecnt++;
trim(buffer);
if(buffer[0] == '#')
continue;
if(!parse_line(&config, buffer))
{
fprintf(stderr, "Error on line %d, ignoring.\n", linecnt);
continue;
}
}
A possible implementation of trim
void rtrim(char *src)
{
size_t i, len;
volatile int isblank = 1;
if(src == NULL) return;
len = strlen(src);
if(len == 0) return;
for(i = len - 1; i > 0; i--)
{
isblank = isspace(src[i]);
if(isblank)
src[i] = 0;
else
break;
}
if(isspace(src[i]))
src[i] = 0;
}
void ltrim(char *src)
{
size_t i, len;
if(src == NULL) return;
i = 0;
len = strlen(src);
if(len == 0) return;
while(src[i] && isspace(src[i]))
i++;
memmove(src, src + i, len - i + 1);
return;
}
void trim(char *src)
{
rtrim(src);
ltrim(src);
}
There are a few ways that you can improve performance:
Calling strstr() in this scenario is inefficient, because the presence of the "host" part of buf can be checked once instead of multiple times every time strstr() is called. Instead, make an if statement that checks if buf begins with "host", then check if buf contains the other elements. The same thing applies to the portion of code checking for the presence of "port".
In the loop in main, instead of doing this:
for(i = 0; i < strlen(buffer); i++) { // iterate through the chars in a line
if(buffer[i] == '#') { // if char is a #, stop processing chars on this line
break;
} else if(buffer[i] == ' ') { // if char is whitespace, continue until something is found
continue;
} else {
parse_line(buffer); // if char is not a # and not whitespace, it is a config directive, parse it
break;
}
do this:
for(i = 0; i < strlen(buffer); i++) { // iterate through the chars in a line
char temp = buffer[i];
if(temp == '#') { // if char is a #, stop processing chars on this line
break;
} else if (temp != ' ') {
parse_line(buffer); // if char is not a # and not whitespace, it is a config directive, parse it
break;
}
Checking to see if something is not equal to another is likely to be just as fast as checking if they are equal (at least on Intel, the je (jump equal) and jne (jump not equal) instructions exhibit the same latency of 1 cycle each), so the statement with the continue in it is not necessary. The temp variable is so that buffer[i] does not need to be calculated in the second if again in case the first if is false. Also, do what user3121023 stated below (same reason for performance as creating the temp variable).
You can use operating-system-specific functions (such as thos from the library WINAPI/WIN32/WIN64 (synonyms) on windows) instead of C standard library functions. Microsoft has very good documentation about their functions in the MSDN (Microsoft Developer Network) web site.
Use uint_fast8_t (defined in stdint.h, this typedef is set to the fastest integer type greater than or equal to the size in bits specified in the typedef) when performing operations on the host and port (but use chars when storing the variables on the disk, in order to make read i/o operations faster).
This isn't related to performance , but use return EXIT_SUCCESS; in main instead of return 0;, since using EXIT_SUCCESS is more readable and exhibits the same performance.
Honestly, I can't help but wonder if rolling your own parser is so great.
Why not use an existing JSON or YAML parser and test for keys in the parsed data?
This will be easily extendible by allowing for new keys to be added with very little effort and the common format of the configuration file makes it very easy for developers to edit.
If you are going to roll out your own parser, than some of the previously mentioned advice makes a lot of sense.
The biggest ones are: don't seek the whole buffer, read the single line that's in front of you and report any errors. Also, advance as you go.
Your parser should work correctly if someone would dump a GigaByte of garbage into the configuration file, so make no assumptions about the data.
For a homework assignement, I need to fill a list of Student structs, which look like the following:
struct Student {
int matriculationNumber;
char *firstName;
char *lastName;
char *birthday;
double averageGrage;
}
The actual data has to be read from a .csv file and looks somethink like
2345678;Meier;Hans;12.10.1985;2,4
1234567;Müller;Fritz;17.05.1990;1,9
For reading in the data, fgetc() should be used.
Now, the problem is how do I actually fill in the fields of the struct and how to handle exceptional conditions (i.e. unexpected EOF; think for example if a line does not contain a field birthday or a field averageGroup).
This is how I'd do it intuitively (which is most probably the wrong way ;-)):
Student student;
if (fillMatriculationNumber(&student, fp) == -1) { // return -1 on failure or EOF
goto failure;
}
if (fillFirstName(&student, fp) == -1) {
goto failure;
}
if (fillLastName(&student, fp) == -1) {
goto failure;
}
if (fillBirthday(&student, fp) == -1) {
goto failure;
}
if (fillAverageGrade(&student, fp) == -1) {
goto failure;
}
// OK
:failure
// print a message about what's wrong, and exit()
I would go in this order:
first read the whole line
then check that the number of field is correct (counting ;should be fine for your example) and handle error situation (skip line or stop parsing?)
then split the line in a char*[] (you can do it in place by placing '\0' and using directly the string or by creating new strings)
then check the required fields for correctness (matriculation is a number, birthday is a date, etc)
then fill the real struct (you could use strcpy, strdup or copying directly the pointer for strings according to your needs)
Since stress is there in fgetc(), you can change your code slightly.
while(!feof(fp)) {
readRecordSuccess = 0;
if (fillMatriculationNumber(&student, fp) != -1) { // return -1 on failure or EOF
if (fillFirstName(&student, fp) != -1) {
if (fillLastName(&student, fp) != -1) {
if (fillBirthday(&student, fp) != -1) {
if (fillAverageGrade(&student, fp) != -1) {
readRecordSuccess = 1;
}
}
}
}
}
if(readRecordSuccess == 0) {
/* may clean already filled structure(s) */
break;
}
/*
* the structure will be overwritten in the next iteration
* take proper measure
*/
}
I would read each CSV row, and then store it in a Student struct.
const unsigned int MaxFields = 5;
const unsigned int MaxContents = 80;
void readRow(FILE * f, char dataRow[MaxFields][MaxContents])
{
int c;
unsigned int i;
char buffer[MaxContents];
int pos;
int field;
// Empty all fields
for(i = 0; i < MaxFields; ++i) {
dataRow[ i ][ 0 ] = '\0';
}
// Read rows
buffer[ 0 ] = '\0';
c = fgetc( f );
pos = 0;
field = 0;
while( c != EOF
&& c != '\n' )
{
if ( c != ';' ) {
buffer[ pos++ ] = c;
} else {
buffer[ pos ] = '\0';
strcpy( dataRow[ field++ ], buffer );
buffer[ 0 ] = '\0';
pos = 0;
}
c = fgetc( f );
}
}
This way, you are reading the contents in a vector of strings. The vector of strings is initialised to the empty string so there is no problem if one field is empty or missing.
Once a row is read, you can store it in a Student struct:
char * safeStrDup(const char * src)
{
char * toret = strdup( src );
if ( toret == NULL ) {
fprintf( stderr, "Not enough memory\n" );
exit( EXIT_FAILURE );
}
return toret;
}
void store(Student *s, char dataRow[MaxFields][MaxContents])
{
s->matriculationNumber = atoi( dataRow[ 0 ] );
s->firstName = safeStrDup( dataRow[ 1 ] );
s->lastName = safeStrDup( dataRow[ 2 ] );
s->birthday = safeStrDup( dataRow[ 3 ] );
s->averageGrage = atof( dataRow[ 4 ] );
}
Take into account that some steps are missing. But this skeleton should give you a good starting point.
Hope this helps.
Initialize the pointer fields in the structure to null pointers; as pointed out in the comments, memset is not the right option here - use c99 way or do it explicitly for each field.
If reading a field fails for some reason, you should free the already allocated fields. For example, if reading average fails for a student and you decide to ignore that student's record, you should free his name fields to prevent memory leaks (assuming they're malloc'ed, of course).