Reading File Into Struct Pointer - c

My file format is :
"name#totalMoney#ratio#luckyNumber"
I,m trying to read that into a dynamic struct pointer.(i want to use it like array)
I want to assign all the lines in the file to the struct array by assigning each line to an element of the struct array.My problem is when i compile this code only the first line of the file is read into struct array and nothing else.Thank for your helps..
#include"Person.h"
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include<math.h>
#define NAMSZ 50//LENGTH FOR NAME BUFFER
void test_1(){
People *tmp=NULL;
int lengtPerson=pFileLength();//it returns number of line in the file
tmp=allocate_struct_array(&tmp,lengtPerson);
readPerson(&tmp);
}
void main(){
test_1();
}
#include"Person.h"//it contains declaration of the functions below and
/*typedef struct P{
char *name;
double totalMoney;
double ratio;
double luckyNumber;
} People; */
People *allocate_struct_array(People **parr,int total_line){
return *parr = malloc (total_line * sizeof **parr);
}
int readPerson(People **parr){
char c;
int MAXC=500;//max length of a line in the file
char buf[MAXC];
/* temporary array to hold each line */
int count = 0,
nparr = 0,
count_lines = 0;
FILE *fileptr = fopen ("Kisiler.txt", "r");
if (!fileptr) { /* always validate file open for reading */
perror ("fopen-fileptr");
}
while (nparr <= count_lines && fgets (buf, MAXC, fileptr)) {
char name[NAMSZ]; //temporary array for name
size_t len; // length of name
if (sscanf (buf, "%[^#]#%lf#%lf#%lf",name, &(parr[nparr]->totalMoney),
&(parr[nparr]->ratio),&(parr[nparr]->luckyNumber)) != 4) {
fputs ("error: invalid line format.\n", stderr);
continue;
}
len = strlen (name); // get length of name
parr[nparr]->name = malloc (len + 1); // allocate for name
if (!parr[nparr]->name) { // validate allocation
perror ("malloc-parr[nparr].name");
break;
}
memcpy (parr[nparr]->name, &name, len + 1);
nparr += 1;
}
for (int i = 0; i < nparr; i++) {
printf (" %-20s %8lf %5lf %5lf\n",
parr[i]->name,parr[i]->totalMoney,parr[i]->ratio,parr[i]->luckyNumber);
//free (parr[i].name); /* free strings when done */
}
//free (parr);
return count_lines;
fclose(fileptr);
}

Related

How to Pass array from C to SV using SV-DPI?

My objective is to pass an array from C to SV and print the array contents in SV, I have tried the following C program of converting some text file(data_file.txt) (given full source in the link below) to an array and trying to read back the array by using DPI calls in SystemVeilog(SV), in "C" I have passed the array values to my function(mydisplay) which is inside the main func.(please correct me if I am wrong here) also it seems the array values are not read back to the SV environment as I would expect what could be the reason, is there an efficient way to get back the array in SV?
c code:
void mydisplay(svOpenArrayHandle h) {
int *a;
a =(int*)svGetArrayPtr(h);
for( i=0;i<idx;i++) {
io_printf("C: values[%2zu]=0x%02x\n",i,values[i]);
a[i] = values[i];
}
}
sv code:
program automatic top;
int a[32000];
import "DPI-C" function void mydisplay(inout int h[]);
initial begin
mydisplay(a);
foreach(a[i]) $display("SV after DPI: a[%0d]=%0d",i,a[i]);
end
endprogram
source at EDAplayground
After some trials I've finally found the solution and able to pass the processed text data from C to SV by a small tweak, I just imported a function and called the exported function using already available context method in the SV-DPI as per LRM, also only used the user defined function and removed the main from native "C" lang., this way I was able to read back the values in the array values from C to SV, provided below the code samples, updated code at EDAPlayground
C code:
#include <stdio.h>
#include <stdlib.h> /* for strtol */
#include <string.h> /* for strchr */
#include <limits.h> /* for INT_MIN/INT_MAX */
#include <errno.h> /* for errno */
extern int dV(int r);
#define MAXL 50000
unsigned long xstrtoul (char *p, char **ep, int base);
int mydisplay()
{
int v[15];
int sd;
int d;
FILE *fp = fopen ("data_file.txt", "r");
char line[MAXL] = {0};
unsigned values[MAXL] = {0};
int base = 16;
size_t i, idx = 0;
if (!fp) { /* validate file open */
fprintf (stderr, "error: file open failen '%s'.\n", fp);
return 1;
}
/* read each line in file (up to MAXL chars per-line) */
while (fgets (line, MAXL, fp)) {
char *p = line;
char *ep = p;
char digits[3] = {0};
errno = 0;
/* convert each string of digits into number */
while (errno == 0) {
/* skip any non-digit characters */
if (!(p = strchr (p, 'x'))) break;
strncpy (digits, ++p, 2);
digits[2] = 0; /* nul-terminate */
/* convert string to number */
values[idx++] = (unsigned)xstrtoul (digits, &ep, base);
if (errno || idx == MAXL) { /* check for error */
fprintf (stderr, "warning: MAXL values reached.\n");
break;
}
p += 2;
}
}
if (fp != stdin) fclose (fp);
/* print results */
for (i = 0; i < idx; i++)
{
printf ("C values[%2zu] : 0x%02x\t", i, values[i]);
v[d]=values[i];
sd = dV(v[d]);
}
return(sd);
}
/** string to unsigned long with error checking */
unsigned long xstrtoul (char *p, char **ep, int base)
{
errno = 0;
unsigned long tmp = strtoul (p, ep, base);
/* Check for various possible errors */
if ((errno == ERANGE && (tmp == ULONG_MAX)) ||
(errno != 0 && tmp == 0)) {
perror ("strtoul");
exit (EXIT_FAILURE);
}
if (*ep == p) {
fprintf (stderr, "No digits were found\n");
exit (EXIT_FAILURE);
}
return tmp;
}
SV code:
program automatic top();
int res;
import "DPI" context mydisplay= function int mD();
export "DPI" dV =function mydisplay;
function int mydisplay(input int xyz);
$display ("SV after DPI: %0d\n",xyz);
return -1;
endfunction
initial begin
#5 res=mD();
$display("Finished reading values...\n");
end
endprogram

How to populate Dynamic array with Strings in C

I am doing a project where I have to read in text from a file and then extract every word that is 4 characters long and allocate it into dynamic array.My approach is to create int function that will get number of 4 letter words and return that number , then create another function that will grab that number and create dynamic array consisting of that many elements. The problem with this approach is how to populate that array with words that meet the requirement.
int func1(FILE *pFile){
int counter = 0;
int words = 0;
char inputWords[length];
while(fscanf(pFile,"%s",inputWords) != EOF){
if(strlen(inputWords)==4){
#counting 4 letter words
counter++;
}
}
}
return counter;
}
int main(){
#creating pointer to a textFile
FILE *pFile = fopen("smallDictionary.txt","r");
int line = 0;
#sending pointer into a function
func1(pFile);
fclose(pFile);
return 0;
}
I would suggest reading lines of input with fgets(), and breaking each line into tokens with strtok(). As each token is found, the length can be checked, and if the token is four characters long it can be saved to an array using strdup().
In the code below, storage is allocated for pointers to char which will store the addresses of four-letter words. num_words holds the number of four-letter words found, and max_words holds the maximum number of words that can currently be stored. When a new word needs to be added, num_words is incremented, and if there is not enough storage, more space is allocated. Then strdup() is used to duplicate the token, and the address is assigned to the next pointer in words.
Note that strdup() is not in the C Standard Library, but that it is POSIX. The feature test macro in the first line of the program may be needed to enable this function. Also note that strdup() allocates memory for the duplicated string which must be freed by the caller.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SZ 1000
#define ALLOC_INC 100
int main(void)
{
FILE *fp = fopen("filename.txt", "r");
if (fp == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char buffer[BUF_SZ];
char **words = NULL;
size_t num_words = 0;
size_t max_words = 0;
char *token;
char *delims = " \t\r\n";
while (fgets(buffer, sizeof buffer, fp) != NULL) {
token = strtok(buffer, delims);
while (token != NULL) {
if (strlen(token) == 4) {
++num_words;
if (num_words > max_words) {
max_words += ALLOC_INC;
char **temp = realloc(words, sizeof *temp * max_words);
if (temp == NULL) {
perror("Unable to allocate memory");
exit(EXIT_FAILURE);
}
words = temp;
}
words[num_words-1] = strdup(token);
}
token = strtok(NULL, delims);
}
}
if (fclose(fp) != 0) {
perror("Unable to close file");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < num_words; i++) {
puts(words[i]);
}
/* Free allocated memory */
for (size_t i = 0; i < num_words; i++) {
free(words[i]);
}
free(words);
return 0;
}
Update
OP has mentioned that nonstandard functions are not permitted in solving this problem. Though strdup() is POSIX, and both common and standard in this sense, it is not always available. In such circumstances it is common to simply implement strdup(), as it is straightforward to do so. Here is the above code, modified so that now the function my_strdup() is used in place of strdup(). The code is unchanged, except that the feature test macro has been removed, the call to strdup() has been changed to my_strdup(), and of course now there is a function prototype and a definition for my_strdup():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SZ 1000
#define ALLOC_INC 100
char * my_strdup(const char *);
int main(void)
{
FILE *fp = fopen("filename.txt", "r");
if (fp == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char buffer[BUF_SZ];
char **words = NULL;
size_t num_words = 0;
size_t max_words = 0;
char *token;
char *delims = " \t\r\n";
while (fgets(buffer, sizeof buffer, fp) != NULL) {
token = strtok(buffer, delims);
while (token != NULL) {
if (strlen(token) == 4) {
++num_words;
if (num_words > max_words) {
max_words += ALLOC_INC;
char **temp = realloc(words, sizeof *temp * max_words);
if (temp == NULL) {
perror("Unable to allocate memory");
exit(EXIT_FAILURE);
}
words = temp;
}
words[num_words-1] = my_strdup(token);
}
token = strtok(NULL, delims);
}
}
if (fclose(fp) != 0) {
perror("Unable to close file");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < num_words; i++) {
puts(words[i]);
}
/* Free allocated memory */
for (size_t i = 0; i < num_words; i++) {
free(words[i]);
}
free(words);
return 0;
}
char * my_strdup(const char *str)
{
size_t sz = strlen(str) + 1;
char *dup = malloc(sizeof *dup * sz);
if (dup) {
strcpy(dup, str);
}
return dup;
}
Final Update
OP had not posted code in the question when the above solution was written. The posted code does not compile as is. In addition to missing #includes and various syntax errors (extra braces, incorrect comment syntax) there are a couple of more significant issues. In func1(), the length variable is used uninitialized. This should be large enough so that inputWords[] can hold any expected word. Also, width specifiers should be used with %s in scanf() format strings to avoid buffer overflow. And, OP code should be checking whether the file opened successfully. Finally, func1() returns a value, but the calling function does not even assign this value to a variable.
To complete the task, the value returned from func1() should be used to declare a 2d array to store the four-letter words. The file can be rewound, but this time as fscanf() retrieves words in a loop, if a word has length 4, strcpy() is used to copy the word into the array.
Here is a modified version of OP's code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_WORD 100
int func1(FILE *pFile){
int counter = 0;
char inputWords[MAX_WORD];
while(fscanf(pFile,"%99s",inputWords) != EOF) {
if(strlen(inputWords) == 4) {
counter++;
}
}
return counter;
}
int main(void)
{
FILE *pFile = fopen("filename.txt","r");
if (pFile == NULL) {
perror("Unable to open file");
exit(EXIT_FAILURE);
}
char inputWords[MAX_WORD];
int num_4words = func1(pFile);
char words[num_4words][MAX_WORD];
int counter = 0;
rewind(pFile);
while(fscanf(pFile,"%99s",inputWords) != EOF) {
if(strlen(inputWords) == 4) {
strcpy(words[counter], inputWords);
counter++;
}
}
if (fclose(pFile) != 0) {
perror("Unable to close file");
}
for (int i = 0; i < num_4words; i++) {
puts(words[i]);
}
return 0;
}

Saving a string from a text file to a struct using fscanf (C)

Sample Text file:
234765 PETER
867574 SMITH
I'm trying to take the id and string from the text file and save it into a struct. The id is saving fine but the string isn't.
typedef struct student
{
int id[DATA_SIZE];
char *student[DATA_SIZE];
}studentinfo;
studentinfo list;
struct student *create_space(int size)
{
struct student *tmp = (struct student*)malloc(size*sizeof(struct student));
return(tmp);
}
struct student * readData(struct student*pointer,studentinfo v)
{
int count =0;
int tmpid;
char str[256];
FILE* in_file;
in_file = fopen("studentlist.txt","r");
while(fscanf(in_file,"%d",&tmpid)!= EOF && count<DATA_SIZE)
{
fscanf(in_file,"%s",v.student[count]);
//printf("%s\n",str );
v.id[count]=tmpid;
count++;
}
pointer =&v;
return pointer;
}
int main()
{
struct student *data;
struct student *sdata;
data = create_space(1);
sdata = readData(data,list);
//printf("%s\n",sdata->student[2] );
}
Their are a couple of issues:
fscanf() reads formatted input, and returns the number of items read.
This line:
while(fscanf(in_file,"%d",&tmpid)!= EOF && count<DATA_SIZE)
Could be this:
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
Which verifies that 2 values are being read on each line successfully.
You are not checking if in_file returns NULL. It's safe to do this. This goes the same for malloc().
You need to correctly create space for char *students[DATA_SIZE], as this is an array of char * pointers. Once you allocate space for this via malloc() or strdup(), then you can copy the contents into students.
Here is an example of doing such a thing:
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
/* allocate space for one student */
list.student[count] = malloc(strlen(str)+1);
if (!list.student[count]) {
printf("Cannot allocate string\n");
exit(EXIT_FAILURE);
}
/* copy it into array */
strcpy(list.student[count], str);
count++;
}
Here is an example that you can use to help achieve your desired result:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DATA_SIZE 256
typedef struct {
int id[DATA_SIZE];
char *student[DATA_SIZE];
} studentinfo_t;
int main(void) {
FILE *in_file;
studentinfo_t list;
char str[DATA_SIZE];
size_t count = 0;
in_file = fopen("studentlist.txt", "r");
if (!in_file) {
fprintf(stderr, "%s\n", "Error reading file");
exit(EXIT_FAILURE);
}
while (count < DATA_SIZE && fscanf(in_file, "%d %255s", &list.id[count], str) == 2) {
list.student[count] = malloc(strlen(str)+1);
if (!list.student[count]) {
printf("Cannot allocate string\n");
exit(EXIT_FAILURE);
}
strcpy(list.student[count], str);
count++;
}
for (size_t i = 0; i < count; i++) {
printf("%d %s\n", list.id[i], list.student[i]);
}
return 0;
}

struct pointers to same memory address producing different data?

I have this simple code to read the lines of a file and store them in a struct:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct filedata {
char **items;
int lines;
};
struct filedata *read_file(char *filename) {
FILE* file = fopen(filename, "r");
if (file == NULL) {
printf("Can't read %s \n", filename);
exit(1);
}
char rbuff;
int nlines = 0; // amount of lines
int chr = 0; // character count
int maxlen = 0; // max line length (to create optimal buffer)
int minlen = 2; // min line length (ignores empty lines with just \n, etc)
while ((rbuff = fgetc(file) - 0) != EOF) {
if (rbuff == '\n') {
if (chr > maxlen) {
maxlen = chr + 1;
}
if (chr > minlen) {
nlines++;
}
chr = 0;
}
else {
chr++;
}
}
struct filedata *rdata = malloc(sizeof(struct filedata));
rdata->lines = nlines;
printf("lines: %d\nmax string len: %d\n\n", nlines, maxlen);
rewind(file);
char *list[nlines];
int buffsize = maxlen * sizeof(char);
char buff[buffsize];
int i = 0;
while (fgets(buff, buffsize, file)) {
if (strlen(buff) > minlen) {
list[i] = malloc(strlen(buff) * sizeof(char) + 1);
strcpy(list[i], buff);
i++;
}
}
rdata->items = (char **)list;
fclose(file);
int c = 0;
for (c; c < rdata->lines; c++) {
printf("line %d: %s\n", c + 1, rdata->items[c]);
}
printf("\n");
return rdata;
}
int main(void) {
char fname[] = "test.txt";
struct filedata *ptr = read_file(fname);
int c = 0;
for (c; c < ptr->lines; c++) {
printf("line %d: %s\n", c + 1, ptr->items[c]);
}
return 0;
}
This is the output when I run it:
lines: 2
max string len: 6
line 1: hello
line 2: world
line 1: hello
line 2: H��
For some reason when it reaches the second index in ptr->items, it prints gibberish output. But yet, if I throw some printf()'s in there to show the pointer addresses, they're exactly the same.
Valgrind also prints this when iterating over the char array the second time:
==3777== Invalid read of size 8
==3777== at 0x400AB3: main (test.c:81)
==3777== Address 0xfff000540 is on thread 1's stack
==3777== 240 bytes below stack pointer
But that really doesn't give me any clues in this case.
I'm using gcc 4.9.4 with glibc-2.24 if that matters.
list is an non-static local variable and using it after exiting its scope (returning from read_file in this case) will invoke undefined behavior because it will vanish on exiting its scope. Allocate it dynamically (typically on the heap) like
char **list = malloc(sizeof(char*) * nlines);
Adding code to check if malloc()s are successful will make your code better.
The variable list is local to read_file, but you store a pointer to list in rdata->items. When read_file returns, rdata->items is a dangling pointer, and accessing it is undefined behavior.

How to sort lines of file depending on value at end of each line

I'm trying to create a program that takes an input file and sorts it to a new output file in ascending order depending on the number at the end of each line. For example, if the input file contains three lines below:
a good man 50
65
better are 7
The corresponding sorted output file would be three lines but sorted:
better are 7
a good man 50
65
Code I have so far:
int sortLines(char * inputFileName, char * outputFileName)
{
FILE *fpin = fopen(inputFileName, "r");//open file to to read
if (!fpin)
{
printf("Error in file opening\n");
exit (-1);
}
FILE *fpout = fopen(outputFileName, "w");//open file to to write
if (!fpout)
{
printf("Error in opfile opening\n");
exit (-1);
}
char file[10][1024];
int i = 0;
while(fgets(file[i], sizeof(file[i]), fpin))
i++;
int total = i;
for(i = 0; i<total; ++i)
printf("%s", file[i]);
return 0;
}
Continuing on from the comment, you can read the lines into a struct (containing the line and an int), then use strrchr to find the last space in each line (or if null, just take the whole line), convert the string with strtol or atoi or the like to set the int field of struct. Then it is a simple matter of sorting the structs based on the int member. I'll leave the reading into the struct to you, the sorting example is:
#include <stdio.h>
#include <stdlib.h>
#define MAXL 32
struct data {
char line[MAXL];
int n;
};
int compare_n (const void *a, const void *b)
{
struct data *ia = (struct data *)a;
struct data *ib = (struct data *)b;
return (int)(ia->n - ib->n);
}
int main (void)
{
struct data lines[] = {{"a good man 50", 50}, {"65", 65}, {"better are 7", 7}};
size_t nstr = sizeof lines / sizeof *lines;
size_t i = 0;
qsort (lines, nstr, sizeof *lines, compare_n);
for (i = 0; i < nstr; i++)
printf (" %s\n", lines[i].line);
return 0;
}
Output
$ ./bin/struct_sort_int
better are 7
a good man 50
65
full example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 64
/* simple struct holding char array and int */
struct data {
char line[MAXL];
int n;
};
/* qsort comparison function for int 'n' */
int compare_n (const void *a, const void *b)
{
struct data *ia = (struct data *)a;
struct data *ib = (struct data *)b;
return (int)(ia->n - ib->n);
}
int main (int argc, char **argv)
{
if (argc < 2 ) { /* validate at least 1 argument provided */
fprintf (stderr, "error: insufficient input, usage: %s filename\n", argv[0]);
return 1;
}
struct data lines[MAXL] = {{{0}, 0}}; /* array of struct */
char *ln = NULL; /* buffer for getline, getline allocates */
size_t n = 0; /* initial size of buf, 0 getline decides */
ssize_t nchr = 0; /* getline return, no. of chars read */
size_t idx = 0; /* index for array of struct */
size_t i = 0; /* general iteration variable */
FILE *fp = NULL; /* file pointer for input file */
if (!(fp = fopen (argv[1], "r"))) { /* validate file open */
fprintf (stderr, "error: file open failed. '%s'\n", argv[1]);
return 1;
}
/* read each line in file */
while ((nchr = getline (&ln, &n, fp)) != -1)
{
while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
ln[--nchr] = 0; /* strip newline or carriage rtn */
if (!nchr) continue; /* skip blank lines */
if (nchr > MAXL - 1) { /* test for line > MAXL -1 */
fprintf (stderr,
"warning: line will exceeded %d chars.\n", MAXL);
continue; /* number at end invalid */
}
strcpy (lines[idx].line, ln); /* copy to struct.line */
char *p = NULL;
if (!(p = strrchr (ln, ' '))) /* pointer to last space */
p = ln; /* if no space, then line */
lines[idx].n = atoi (p); /* convert string to int */
idx++; /* increment index */
if (idx == MAXL) { /* if MAXL read, break */
fprintf (stderr, "warning: %d lines read.\n", MAXL);
break;
}
}
if (fp) fclose (fp); /* close input file */
if (ln) free (ln); /* free line buffer mem */
qsort (lines, idx, sizeof *lines, compare_n); /* sort struct */
for (i = 0; i < idx; i++) /* print sorted array */
printf (" %s\n", lines[i].line);
return 0;
}
Take a look and let me know if you have questions. Your data was in the file dat/endno.txt for my test. I'll add comments when I get a chance.
note: updated to skip blank lines and to check line length against MAXL to eliminate the possibility of a write beyond end of lines and skip lines that would be truncated rendering the number at end invalid.
without struct statically allocated arrays
The following is an example that uses two 2D arrays, one for the lines and then one holding the original line index and number at end of line. Unlike the dynamically allocated example below, this example is limited to reading MAXL lines from the file or no more than MAXS characters each. If a line is exactly MAXS characters long (including the null-terminator), it must be discarded, because there is no way of knowing if the number at end remains valid. The 2D array containing the line index and number at end is sorted based on the number at end, then lines are printed based on the original line index resulting in the lines printing in sorted order by number at end. While this may look simpler, it is far more limited than the method utilizing the struct or the dynamically allocated approach below. This is about all I can think to do to get your going. Good luck. Drop a line if you have questions.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 64
#define MAXS 128
int cmpint (const void *a, const void *b);
int main (int argc, char **argv) {
if (argc < 2 ) { /* validate at least 1 argument provided */
fprintf (stderr, "error: insufficient input, usage: %s filename\n", argv[0]);
return 1;
}
int numidx[MAXL][2] = {{0}}; /* array of integers */
char lines[MAXL][MAXS] = {{0}}; /* array of strings */
char ln[MAXS] = {0}; /* buffer for fgets, MAXS in length */
ssize_t nchr = 0; /* getline return, no. of chars read */
size_t idx = 0; /* index for array of struct */
size_t i = 0; /* general iteration variable */
FILE *fp = NULL; /* file pointer for input file */
if (!(fp = fopen (argv[1], "r"))) { /* validate file open */
fprintf (stderr, "error: file open failed. '%s'\n", argv[1]);
return 1;
}
/* read each line in file */
while (fgets (ln, MAXS, fp) != NULL)
{
nchr = strlen (ln); /* get length of ln */
while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
ln[--nchr] = 0; /* strip newline or carriage rtn */
if (!nchr || nchr == MAXS - 2) /* skip blank lines + full */
continue; /* lines (end no. invalid) */
strcpy (lines[idx], ln); /* copy ln to lines[idx] */
char *p = NULL;
if (!(p = strrchr (ln, ' '))) /* pointer to last space */
p = ln; /* if no space, then line */
numidx[idx][0] = atoi (p); /* save end no. in array */
numidx[idx][1] = idx; /* save line index in array */
idx++; /* increment index */
if (idx == MAXL) { /* if MAXL read, break */
fprintf (stderr, "warning: %d lines read.\n", MAXL);
break;
}
}
fclose (fp);
qsort (numidx, idx, sizeof (int) * 2, cmpint);/* sort array */
for (i = 0; i < idx; i++) /* print sorted array */
printf (" %s\n", lines[numidx[i][1]]);
return 0;
}
/* qsort integer compare function */
int cmpint (const void *pa, const void *pb )
{
const int *a = pa;
const int *b = pb;
if (a[0] < b[0])
return -1;
return (b[0] < a[0]);
}
without struct, dynamically allocated arrays
To get around using a structure to hold the string an number, you can use 2 arrays. One to hold the strings, and another 2D array holding the original line index and number at end of line (2 integers). You then qsort the integer array on the (number at end) element, then loop through each line printing out the lines in sorted order based on the line index value of the sorted array. This is set to handle lines of any length and reallocate the number of lines (in each array) as needed. Since the dynamic allocation may be a bit much, I'm working on a static array version as well, but it will be tomorrow before I have time. Here is the first version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 64
int cmpint (const void *a, const void *b);
char **realloc_char (char **sp, size_t *n);
int **realloc_int (int **ip, size_t *n);
int main (int argc, char **argv) {
if (argc < 2 ) { /* validate at least 1 argument provided */
fprintf (stderr, "error: insufficient input, usage: %s filename\n", argv[0]);
return 1;
}
int **numidx = NULL; /* array of pointers-to-pointer-to-int */
char **lines = NULL; /* array of pointer-to-pointer-to-char */
char *ln = NULL; /* buffer for getline, getline allocates */
size_t n = 0; /* initial size of buf, 0 getline decides */
ssize_t nchr = 0; /* getline return, no. of chars read */
size_t idx = 0; /* index for array of struct */
size_t i = 0; /* general iteration variable */
size_t maxl = MAXL; /* holds current allocation size of arrays */
FILE *fp = NULL; /* file pointer for input file */
if (!(fp = fopen (argv[1], "r"))) { /* validate file open */
fprintf (stderr, "error: file open failed. '%s'\n", argv[1]);
return 1;
}
/* allocate MAXL pointers to int* */
if (!(numidx = calloc (MAXL, sizeof *numidx))) {
fprintf (stderr, "error: memory allocation failed.\n");
return 1;
}
/* allocate MAXL pointers to char* */
if (!(lines = calloc (MAXL, sizeof *lines))) {
fprintf (stderr, "error: memory allocation failed.\n");
return 1;
}
/* read each line in file */
while ((nchr = getline (&ln, &n, fp)) != -1)
{
while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
ln[--nchr] = 0; /* strip newline or carriage rtn */
if (!nchr) continue; /* skip blank lines */
lines[idx] = strdup (ln); /* copy ln to lines[idx] */
/* allocate space for 2 int at numidx[idx] */
if (!(numidx[idx] = calloc (2, sizeof **numidx))) {
fprintf (stderr, "error: memory allocation failed.\n");
return 1;
}
char *p = NULL;
if (!(p = strrchr (ln, ' '))) /* pointer to last space */
p = ln; /* if no space, then line */
numidx[idx][0] = atoi (p); /* save end no. in array */
numidx[idx][1] = idx; /* save line index in array */
idx++; /* increment index */
if (idx == maxl) { /* if idx = maxl reallocate */
size_t tsz = maxl; /* tmp var, each get maxl */
numidx = realloc_int (numidx, &tsz);
lines = realloc_char (lines, &maxl);
}
}
if (ln) free (ln);
fclose (fp);
qsort (numidx, idx, sizeof *numidx, cmpint); /* sort struct */
for (i = 0; i < idx; i++) /* print sorted array */
printf (" %s\n", lines[numidx[i][1]]);
for (i = 0; i < idx; i++) { /* free allocated memory */
free (numidx[i]);
free (lines[i]);
}
free (numidx);
free (lines);
return 0;
}
/* qsort integer compare function */
int cmpint (const void *a, const void *b)
{
const int **ia = (const int **)a;
const int **ib = (const int **)b;
return (*ia)[0] - (*ib)[0];
}
/** realloc an array of pointers to strings setting memory to 0.
* reallocate an array of character arrays setting
* newly allocated memory to 0 to allow iteration
*/
char **realloc_char (char **sp, size_t *n)
{
char **tmp = realloc (sp, 2 * *n * sizeof *sp);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
sp = tmp;
memset (sp + *n, 0, *n * sizeof *sp); /* memset new ptrs 0 */
*n *= 2;
return sp;
}
/** realloc an array of pointers to int* setting memory to 0.
* reallocate an array of integer arrays setting
* newly allocated memory to 0 to allow iteration
*/
int **realloc_int (int **ip, size_t *n)
{
int **tmp = realloc (ip, 2 * *n * sizeof *ip * 4);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ip = tmp;
memset (ip + *n, 0, *n * sizeof *ip * 4); /* memset new ptrs 0 */
*n *= 2;
return ip;
}
You could read the entire file into a single buffer, create an array of structures containing pointers to lines and the values at the end of each line (scan for newline characters), then sort the array of structures by the values, and output the data according to the pointers in the sorted array of structures.

Resources