Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
275 views
in Technique[技术] by (71.8m points)

c - Using qsort and strcmp prior to my sctrucs going into txt files

My code pulls data from one text file and then totals the points and enters it into a separate text file, so I would like the program to organize the teams and scores by total points prior to frpintf into the text file. So far the program pulls and runs the data and totals it out and fprintf just fine, should I use qsort to sort and print into the text file, and where in my code do I place it. Here is the text it is reading.

Indians 54 45 5

Twins 45 53 7

Tigers 43 59 8

White_Sox 35 64 9

Royals 30 69 3

Also I know there are no ties in the MLB lol just throwing in an additional variable.

Thanks.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main(void)

{
struct records {
     char filename;
     char team[50];
     int wins;
     int tie;
     int loss;
     int points;
};
struct records roster;
     FILE *ifp = NULL;
     FILE  *afd = NULL;
     const int argv;
     char filename2[64] = {0};
     char filename[64] = {0};
     int points;
     int points2;
     int total;


printf("
Please enter the the .txt file you would like to open: ");
scanf("%63s", filename);
printf("Opened file name %s",filename);
ifp = fopen(filename,"r");


 if (ifp == NULL)
{
  printf("Could not open");
  printf("
Please enter the the .txt file you would like to open:");
  scanf("%63s", filename);
  printf("Opened file name %s",filename);
  ifp = fopen(filename,"r");
}

printf("
Reading the file %s 
", filename);
while(fscanf(ifp, "%s %d %d%d" , roster.team, &roster.wins, &roster.loss, 
 &roster.tie) != EOF)
printf("%s Wins:%d Losses:%d ties:%d
", roster.team, roster.wins, 
 roster.loss, roster.tie);
printf("
Wins are worth 2 points ties are worth 1 point and losses are 
 worth 
zero in overall league standings.");
printf("
Here are the overall ALCentral Standings!
");

ifp = fopen(filename, "r");
fopen(filename, "r"); while(fscanf(ifp, "%s %d %d %d", roster.team, 
 &roster.wins, &roster.loss, &roster.tie) != EOF)
printf("%s Wins:%d Losses:%d ties:%d Total league points:%d
", 
 roster.team, roster.wins, roster.loss, roster.tie, (roster.wins * 2 + 
 roster.tie * 1), total);


     printf("closing %s", filename);
     fclose(ifp);

printf("
Please enter the the .txt file you would like to write to: ");
scanf("%63s", filename2);
printf("Opened file name %s", filename2);
afd = fopen(filename2, "a+");

if (afd == NULL)
{
 printf("Could not open"); printf("
Please enter the the .txt file you 
 would like to open: ");
 scanf("%63s", filename2);
 printf("Opened file name %s", filename2);
 afd = fopen(filename2,"a+");
}

ifp = fopen(filename,"r");
fopen(filename, "r");  
points = roster.wins * 2;
points2 = roster.tie * 1; 
total = points + points2;
while(fscanf(ifp, "%s %d %d %d", roster.team, &roster.wins, &roster.loss, 
 &roster.tie) != EOF)



fprintf(afd, "%s Wins:%d Losses:%d ties:%d total league points:%d
", 
roster.team, roster.wins, roster.loss, roster.tie, (roster.wins *2 + 
 roster.tie *1 ), total, filename2);
printf("
Your stats have been recorded to the selected 
file!
Closing all open files! 
Thanks for using the STAT-Master! 
Have a great day!


fclose(afd);
fclose(ifp);



return 0;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Your logic is quite jumbled. If I understand what you are attempting, you want to read each teams wins, loss and tie record from your data file, and then compute the points that are then used to sort the teams into descending (or perhaps ascending) order and then output the "the overall ALCentral Standings" to either stdout or a file, or both.

Before starting let's talk about your definition of struct records inside of main(). While legal, you generally will want your struct declaration to be at file scope so that the struct type is available to any functions you write to help process the data in your code. Here, you must move the declaration outside of main() because the qsort compare function will need to know what the type struct records is. The compare function cannot be declared and defined in main() because C does not allow nested function declarations. So move your struct outside of main(), e.g.

#define MAXR   16   /* if you need a constant, #define one (or more) */
#define MAXC   64
#define MAXB 1024

typedef struct records {
    char team[MAXC];
    int wins,
        loss,
        tie,
        points;
} record_t;

(note: using a typedef can make things more convenient)

To begin with, your multiple attempts to read the filename is awkward and never insures you actually have a file open. It just makes a couple of attempts and then effectively gives up validation and just blindly assumes there is a valid file stream to read and write to and presses ahead. This is a recipe for invoking Undefined Behavior.

When ever you do anything in C, and especially when you are dealing with user input or file operations, you must validate every step. You must know whether or not the file is open for reading before you attempt to read. (no matter how many times you attempt to open it) You must validate what you have read before you begin using the values in your code. You must validate the state of your input stream before you attempt your next read, and handle any characters that remain.

With that in mind you can prompt for the filename and attempt to open the filename provided in a continual loop until you validate the file is actually open, e.g.

/** helper function to empty stdin between inputs */
void empty_stdin()
{
    int c = getchar();

    while (c != '
' && c != EOF)
        c = getchar();
}
...
    FILE *fp = NULL;            /* file pointer */

    do {    /* loop until valid filename given and file open */
        printf("
enter input filename: ");
        if (scanf (" %63[^
]", filename) != 1) {
            fprintf (stderr, "error: user canceled input.
");
            return 1;
        }
        empty_stdin();  /* remove '
' and any add'l chars in input buffer */
        fp = fopen (filename, "r");     /* open filename */
        if (fp == NULL)                 /* validate file open for reading */
            perror ("fopen-input");
    } while (fp == NULL);
    printf("Opened file name %s

",filename);

(note: rather than using scanf for user input, fgets provides a better approach to user input and far fewer pitfalls that scanf, example below with roster)

Next, your understanding of how to collect the data in memory in order to sort it, is not evident from your post. When you want to sort a collection of team information, you must read all team information into memory before you can pass the information to qsort for sorting.

You have a struct that holds all of information for each team, you simply need to read each line (record) from your input file into an array or struct which can then easily be passed to qsort for sorting.

To use qsort you first must define a compare function that is used to qsort to order your data. The compare function has a specific declaration:

int compare (const void *a, const void *b);

Where each const void * parameter will be a pointer to one of the elements in your array of records. (in C a void pointer, e.g. void* is a generic pointer that can be assigned from, or to, any other pointer type without a cast -- that is why the compare function's declaration uses that type for the parameters) Your job in writing a compare function is simply to provide a type to the pointers (either by assignment or by explicit cast, your choice) and then a comparison between the values that you wish to sort by. For example, your compare on the points member of each struct could simply be:

/** qsort compare function (descending order of points) */
int compare (const void *a, const void *b)
{
    const record_t *ta = a;
    const record_t *tb = b;

    return (ta->points < tb->points) - (ta->points > tb->points);
}

(where the (a < b) - (a > b) simply evaluates to -1, 0, 1 depending on the inequalites, e.g. if a = 4 and b = 3, you have (0) - (1) which equals -1 meaning a sorts before b)

All that remains is reading each teams data into your array and computing points and then passing your array to qsort.

To read your data into an array, read each line into a buffer, parse the data in the buffer into the individual values, validate that each of the individual values where parsed correctly, compute the points value for each team, increment your array index and then go read the next line. You could do something like the following using fgets to read each line of data:

    record_t roster[MAXR] = {{ .team = "" }};   /* array to hold teams */
    ...
    /* read up to MAXR team records from file */
    while (ndx < MAXR && fgets (buf, MAXB, fp)) {
        /* parse data from buffer filled, saving return */
        int rtn = sscanf (buf, " %49s %d %d %d" , roster[ndx].team, 
                        &roster[ndx].wins, &roster[ndx].loss, 
                        &roster[ndx].tie);
        if (rtn < 4)    /* if less than 4 conversions, get next line */
            continue;

        /* compute points (2 * wins + ties) */
        roster[ndx].points = roster[ndx].wins * 2 + roster[ndx].tie;
        ndx++;  /* increment index */
    }
    fclose (fp);    /* close file */

(note: you can add a strlen() check of buf to determine if additional characters remain in the line unread -- that is left to you).

With all you data in your array of structs roster, all that remains is passing roster to qsort and then outputting your sorted data.

The following sorts the data and outputs the results to stdout. Writing the output to a file is left to you, but hint, you don't need both ifp and afp and you don't need filename and filename2 or points2, you are only dealing with one file/filename at a time. The sort and output is simple:

    /* sort roster based on points (descending order) */
    qsort (roster, ndx, sizeof *roster, compare);

    /* outpout results */
    printf ("Here are the overall ALCentral Standings!

"
            "Team         Wins  Loss  Tie   Points

");
    for (int i = 0; i < ndx; i++)
        printf ("%-12s %4d  %4d  %4d  %4d
", roster[i].team, 
                roster[i].wins, roster[i].loss, roster[i].tie,
                roster[i].points);

Putting it altogether in an example, you could do something like the following:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXR   16   /* if you need a constant, #define one (or more) */
#define MAXC   64
#define MAXB 1024

typedef struct records {
    char team[MAXC];
    int wins,
        loss,
        tie,
        points;
} record_t;

/** helper function to empty stdin between inputs */
void empty_stdin()
{
    int c = getchar();

    while (c != '
' && c != EOF)
        c = getchar();
}

/** qsort compare function (descending order of points) */
int compare (const void *a, const void *b)
{
    const record_t *ta = a;
    const record_t *tb = b;

    return (ta->points < tb->points) - (ta->points > tb->points);
}

int main (void) {

    int ndx = 0;                /* index for roster array */
    char buf[MAXB] = "",        /* buffer for fgetrs */
        filename[MAXC] = "";    /* filename to read */
    record_t roster[MAXR] = {{ .team = "" }};   /* array to hold teams */
    FILE *fp = NULL;            /* file pointer */

    do {    /* loop until valid filename given and file open */
        printf("
enter input filename: ");
        if (scanf (" %63[^
]", filename) != 1) {
            fprintf (stderr, "error: user canceled input.
");
            return 1;
        }
        empty_stdin();  /* remove '
' and any add'l chars in input buffer */
        fp = fopen (filename, "r");     /* open filename */
        if (fp == NULL)                 /* validate file open for reading */
            perror ("fopen-input");
    } while (fp == NULL);
    printf("Opened file name %s

",filename);

    /* read up to MAXR team records from file */
    while (ndx < MAXR && fgets (buf, MAXB, fp)) {
        /* parse data from buffer filled, saving return */
        int rtn = sscanf (buf, " %49s %d %d %d" , roster[ndx].team, 
                        &roster[ndx].wins, &roster[ndx].loss, 
                        &roster[ndx].tie);
        if (rtn < 4)    /* if less than 4 conversions, get next line */
            continue;

        /* compute points (2 * wins + ties) */
        roster[ndx].points = roster[ndx].wins * 2 + roster[ndx].tie;
        ndx++;  /* increment index */
    }
    fclose (fp);    /* close file */

    /* sort roster based on points (descending order) */
    qsort (roster, ndx, sizeof *roster, compare);

    /* outpout results */
    printf ("Here are the overall ALCentral Standings!

"
            "Team         Wins  Loss  Tie   Points

");
    for (int i = 0; i < ndx; i++)
        printf ("%-12s %4d  %4d  %4d  %4d
", roster[i].team, 
                roster[i].wins, roster[i].loss, roster[i].tie,
                roster[i].points);

    return 0;
}

Example Input File

The file is provided as given, with blank lines in between records.

$ cat dat/teams.txt
Indians 54 45 5

Twins 45 53 7

Tigers 43 59 8

White_Sox 35 64 9

Royals 30 69 3

Example Use/Output

$ ./bin/qsortstrcmp

enter input filename: dat/teams.txt
Opened file name dat/teams.txt

Here are the overall ALCentral Standings!

Team         Wins  Loss  Tie   Points

Indians        54    45     5   113
Twins          45    53     7    97
Tigers         43    59     8    94
White_Sox      35    64     9    79
Royals         30    69     3    63

Look things over and let me know if you have further questions.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...