Royce Cornwell
Royce Cornwell

Reputation: 31

How to sort an array read from a file in ascending order using pointers

This is in C language, I'm trying to sort the data from the 2015 population in the text file in ascending order using a function and pointers to the array that I stored the state in. I want to try use a swapping algorithm within this function.

How do I sort the data using my ascensionOrder function from the 2015 population then output it in acending order?

Here is my code: `

#include <stdio.h>
#define FILENAME "PopulationData.txt"

int main(void)
{   
void acensionOrder(int *populationData);
void replace(char*s, char a, char b);
char header[3][30];
char state[51][32];
int census[51][2];

FILE *myfile;
myfile = fopen(FILENAME,"r");
int x;
if (myfile== NULL)
{
    printf("Errror opening file. \n");
}
else
{
    fscanf(myfile, "%s%s%s", header[0], header[1], header[2]);

    for (x = 0; x < 51; x++)
    {   
        //fscanf(myfile, "%31s%d%d", state[x], &census[x][0], &census[x][1]); //testing
        fscanf(myfile, "%*2c%s %d %d", state[x], &census[x][0], &census[x][1]); //Stores the text in the data file into array
        replace(state[x], '_', ' '); //Replaces the lines
        replace(state[x], '.', ' '); //Replaces the periods
        //printf("%s\t%d\t%d\n", state[x], census[x][0], census[x][1]);
        //printf("%2d %31s %10d %10d\n", x, state[x], census[x][0], census[x][1]); //Testing of sort
    }
}   
acensionOrder(&census[x][1]);
fclose(myfile);


//getchar();
//getchar();

return 0;
}

void acensionOrder(int *populationData) //This function sorts the 2015 data           into ascending order
 {
int j,k,m;
int sorted2015;
for(k=0; k<m; k++)
{   
    //m=k;      
    for(j=0; j<(m-1); j++)
    {
        if(*(populationData+j)<*(populationData+m))

            //m=j;
            sorted2015=*(populationData+m);
            *(populationData+m)=*(populationData+k);
            *(populationData+k)=sorted2015;
    }
}
    printf("%d\n", sorted2015);
}

void replace(char*s, char a, char b) //This function uses pointers to find     characters
{ 
for(;*s; s++)
{       
    if(*s==a)*s = b;
}
}

`

Here is the text file the program reads from: Text file for US population

Upvotes: 2

Views: 1379

Answers (3)

David C. Rankin
David C. Rankin

Reputation: 84551

I'm trying to sort the data from the 2015 population in the text file in ascending order using a function and pointers to the array

As a number of comments have pointed out, you can accomplish your stated goal simply by using the qsort algorithm. The comparison function you write for qsort will accept a pointer to an element within any type of array of objects you pass to it. In your case, holding the data in an array of struct containing the name of the state, the census and estimate would make things quite easy. You could use a simple struct with a single character array (24-char will hold your longest name) and two integers. Adding a typedef to stcen for your state census is for convenience, e.g.

typedef struct {       /* structure with name census and estimate */
    char name[NMLEN];
    int cen, est;
} stcen;

Using qsort, you simply want an ascending sort on an integer value, which for a standard integer array would take the form:

int cmpint (const void *a, const void *b)
{
    /* (a > b) - (a < b) */
    return (*(int *)a > *(int *)b) - (*(int *)a < *(int *)b);
}

which if you prefer to cast before the return, it is just shorthand for:

const int ia = *(const int *)a; // casting pointer types
const int ib = *(const int *)b;
return (ia > ib) - (ia < ib);

(returning the difference of the comparison, avoids potential for overflow)

Using an array of struct is no different, you simply dereference the pointers passed to the comparison function until you are referencing the integer value you wish to sort by, e.g. in the case above:

/* integer comparison of struct on 'cen' */
int cmpcen (const void *a, const void *b) {
    return ((((stcen *)a)->cen > ((stcen *)b)->cen) -
        (((stcen *)a)->cen < ((stcen *)b)->cen));
}

The remainder is just reading your data from your data file, discarding the heading row, and storing the remaining values in your array of struct. Since you know you have 51 states, your longest state name will fit in 24-chars (and a formatting integer width of 9-chars to make printing pretty) you can use an enum to specify the constants for your program. Putting the pieces together, you can do something similar to the following:

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

enum { IWDTH = 9, NMLEN = 24, NSTATE = 51 };    /* constants used */

typedef struct {       /* structure with name, census and estimate */
    char name[NMLEN];
    int cen, est;
} stcen;

/* integer comparison of struct on 'cen' */
int cmpcen (const void *a, const void *b) {
    return ((((stcen *)a)->cen > ((stcen *)b)->cen) -
        (((stcen *)a)->cen < ((stcen *)b)->cen));
}

int main (int argc, char **argv) {

    int i = 0, ndx = 0;     /* general i, index */
    stcen census[NSTATE] = {{ .name = "" }}; /* array of struct */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "file open failed '%s'\n", argv[1]);
        return 1;
    }
    fscanf (fp, "%*[^\n]%*c");  /* strip header line */

    /* read each line, input to struct, advance index */
    while (ndx < NSTATE && fscanf (fp, " %s %d %d",
        census[ndx].name, &census[ndx].cen, &census[ndx].est) == 3) {
        ndx++;
    }
    if (fp != stdin) fclose (fp);       /* close file if not stdin */

    qsort (census, ndx, sizeof *census, cmpcen); /* sort on census */

    for (i = 0; i < ndx; i++)   /* output results */
        printf (" %-*s  %*d  %*d\n", NMLEN, census[i].name,
                IWDTH, census[i].cen, IWDTH, census[i].est);

    return 0;
}

The program simply expects the name of the file to read data from as the first argument (or it will read from stdin if no argument is given). Applied to your data, you get:

Example Use/Output

$ ./bin/census < ../dat/census.txt
 Wyoming                      563626     586107
 District_of_Columbia         601723     672228
 Vermont                      625741     626042
 North_Dakota                 672591     756927
 Alaska                       710231     738432
 South_Dakota                 814180     858469
 Delaware                     897934     945934
 Montana                      989415    1032949
 Rhode_Island                1052567    1056298
 New_Hampshire               1316470    1330608
 Maine                       1328361    1329328
 Hawaii                      1360301    1431603
 Idaho                       1567582    1654930
 Nebraska                    1826341    1896190
 West_Virginia               1852994    1844128
<snip>

Look things over and consider the qsort approach instead of rolling-your-own sort routine. The chances are very good that qsort will be much more efficient and less prone to errors. Let me know if you have any questions.

Upvotes: 1

Jack
Jack

Reputation: 11

#include <stdio.h>
#define FILENAME "C:\\PopulationData.txt"
#define NUM 51
int main(void)
{
void acensionOrder(int *populationData);
void replace(char*s, char a, char b);
char header[3][30];
char state[NUM][32];
int census[2][NUM];

FILE *myfile;
myfile = fopen(FILENAME, "r");
int x;
if (myfile == NULL)
{
    printf("Errror opening file. \n");
}
else
{
    fscanf(myfile, "%s%s%s", header[0], header[1], header[2]);

    for (x = 0; x < NUM; x++)
    {
        //fscanf(myfile, "%31s%d%d", state[x], &census[x][0], &census[x][1]); //testing
        fscanf(myfile, "%*2c%s %d %d", state[x], &census[0][x], &census[1][x]); //Stores the text in the data file into array
        replace(state[x], '_', ' '); //Replaces the lines
        replace(state[x], '.', ' '); //Replaces the periods
        //printf("%s\t%d\t%d\n", state[x], census[x][0], census[x][1]);
        //printf("%2d %31s %10d %10d\n", x, state[x], census[x][0], census[x][1]); //Testing of sort
    }
}
acensionOrder(&census[1][0]);
fclose(myfile);


getchar();
getchar();

return 0;
}

void acensionOrder(int *populationData) //This function sorts the 2015 data           into ascending order
{
int j, k, m;
m = NUM;
int sorted2015;

for (k = 0; k<m; k++)
{
    //m=k;      
    for (j = k+1; j<m; j++)
    {
        if (*(populationData + j) < *(populationData + k ))
        {
            sorted2015 = *(populationData + j);
            *(populationData + j ) = *(populationData + k);
            *(populationData + k) = sorted2015;
        }

            //m=j;

    }
}
for (k = 0; k<m; k++)
    printf("%d ", *(populationData + k));
}

void replace(char*s, char a, char b) //This function uses pointers to find     characters
{
for (; *s; s++)
{
    if (*s == a)*s = b;
}
}

Upvotes: 0

hmofrad
hmofrad

Reputation: 1902

There was a couple of bugs in your program especially in the acensionOrder() function which I completely changed it to a bubbleSort() that keeps swapping the array elements until it is sorted. Keep in mind bubbleSort() is easy but expensive. So, you may want to change it to a o(nlogn) sorting algorithm like quicksort() or mergesort(). Now, let's see what do you need to add to make your program sort the input file.

Before reaching the main() function you need to import some standard C libraries and also declare your function signatures. Previously, you put the function signatures in the main() function which was wrong.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILENAME "PopulationData.txt"

void bubbleSort(int arr1[][2], char arr2[][32], int len);
void replace(char *s, char a, char b);

For main(), make sure to add the argc, *argv[] arguments. Basically, in the main() function, we open the file, start parsing each line using fscanf(), trim the . and _ characters, close the file, call the bubbleSort() and finally print the sorted results.

int main(int argc, char *argv[])
{
   char header[3][30];
   memset(header, 0, sizeof(header[0][0]) * 3 * 30);
   char state[51][32];
   memset(state, 0, sizeof(state[0][0]) * 51 * 32);
   int census[51][2];
   memset(census, 0, sizeof(census[0][0]) * 51 * 2);

   FILE *myfile;
   myfile = fopen(FILENAME,"r");
   int x;
   if (myfile== NULL)
   {
       printf("Errror opening file. \n");
       return(1);
   }

   fscanf(myfile, "%s %s %s", header[0], header[1], header[2]);
   printf("%s %s %s\n",header[0], header[1], header[2]);
   for (x = 0; x < 51; x++)
   {
      fscanf(myfile, "%*2c%s %d %d", state[x], &census[x][0], &census[x][1]);
      replace(state[x], '_', ' ');
      replace(state[x], '.', ' ');
      printf("[%02d] %20s: %8d %8d\n", x, state[x], census[x][0], census[x][1]);
   }
   fclose(myfile);

   bubbleSort(census, state, 51);
   printf("%s %s %s\n",header[0], header[1], header[2]);
   for(x = 0; x < 51; x++)
   {   
      printf("[%02d] %20s: %8d %8d\n", x, state[x], census[x][0], census[x][1]);
   }
   return(0);
}

The next part is the abubbleSort() implementation. It accepts the census and state arrays along with their length and starts sorting them based on the second dimension of census (censos[j][1]) which contains the 2015 population. Each time, we want to replace an element in censos[j][1], we need to replace the corresponding elements in censos[j][0] and state[j].

void bubbleSort(int arr1[][2], char arr2[][32], int len)
{
   int i;
   int j;
   int tmp0;
   int tmp1;
   char tmp2[32];
   memset(tmp2, '\0',32);
   for(i = len - 1; i >= 0; i--)
   {
      for(j = 0; j < i; j++)
      {
         if(arr1[j][1] > arr1[j+1][1])
         {
            tmp0 = arr1[j+1][0];
            tmp1 = arr1[j+1][1];
            strncpy(tmp2, arr2[j+1], 32);

            arr1[j+1][0] = arr1[j][0];
            arr1[j+1][1] = arr1[j][1];
            strncpy(arr2[j+1], arr2[j], 32);

            arr1[j][0] = tmp0;
            arr1[j][1] = tmp1;
            strncpy(arr2[j], tmp2, 32);

            memset(tmp2, '\0',32);
         }
      }
   }
}

And this is your replace() function which I'm pasting it here AS IS.

void replace(char*s, char a, char b)
{ 
   for(;*s; s++)
   {       
      if(*s==a)*s = b;
   }
}

Finally, since you're dealing with strings, you may notice that I extensively use memset() to make the array elements NULL terminated to avoid plausible printing problems with printf() function.

Upvotes: 2

Related Questions