【问题标题】:How to sort an array read from a file in ascending order using pointers如何使用指针对从文件中读取的数组进行升序排序
【发布时间】:2017-04-29 06:31:42
【问题描述】:

这是用 C 语言编写的,我正在尝试使用函数和指向存储状态的数组的指针对文本文件中 2015 年人口中的数据进行升序排序。我想尝试使用交换此函数内的算法。

如何使用我的 ascensionOrder 函数对 2015 年人口中的数据进行排序,然后按升序输出?

这是我的代码:`

#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;
}
}

`

这是程序读取的文本文件: Text file for US population

【问题讨论】:

  • “如何使用我的 ascensionOrder 函数对数据进行排序”。调用函数?如果您说该功能不起作用,请描述具体行为。
  • 你以前用过qsort吗?我还建议将您的数据存储在某种 struct 中。
  • 如果您发布您的文本文件并正确格式化它也会更容易。它与间距不一致,坦率地说很难阅读。
  • @RoadRunner 间距似乎与制表符一致
  • m 未在 int j,k,m; ...for(k=0; k&lt;m; k++) 中初始化

标签: c arrays file sorting text


【解决方案1】:

您的程序中有几个错误,尤其是在acensionOrder() 函数中,我将它完全更改为bubbleSort(),它会不断交换数组元素直到它被排序。请记住bubbleSort() 简单但昂贵。因此,您可能希望将其更改为 o(nlogn) 排序算法,例如 quicksort()mergesort()。现在,让我们看看你需要添加什么来让你的程序对输入文件进行排序。

在到达main() 函数之前,您需要导入一些标准C 库并声明您的函数签名。以前,您将函数签名放在 main() 函数中,这是错误的。

#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);

对于main(),请确保添加argc*argv[] 参数。基本上,在main() 函数中,我们打开文件,使用fscanf() 开始解析每一行,修剪._ 字符,关闭文件,调用bubbleSort(),最后打印排序结果。

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);
}

下一部分是abubbleSort() 的实现。它接受 censusstate 数组及其长度,并根据包含 2015 年人口的人口普查 (censos[j][1]) 的第二维开始对它们进行排序。每次我们要替换censos[j][1]中的一个元素,都需要替换censos[j][0]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);
         }
      }
   }
}

这是您的 replace() 函数,我将其按原样粘贴在这里。

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

最后,由于您正在处理字符串,您可能会注意到我广泛使用memset() 来使数组元素以NULL 结尾,以避免printf() 函数出现似是而非的打印问题。

【讨论】:

    【解决方案2】:

    我正在尝试对文本文件中 2015 年人口的数据进行排序 使用函数和指向数组的指针按升序排列

    正如许多 cmets 所指出的,您只需使用qsort 算法即可实现您的既定目标。您为qsort 编写的比较函数将接受指向您传递给它的任何类型的对象数组中的元素的指针。在您的情况下,将数据保存在 struct 数组中,其中包含州的 namecensusestimate 会让事情变得很简单。您可以使用带有单个字符数组(24 个字符将保存您的最长名称)和两个整数的简单结构。为您的州人口普查添加typedefstcen 是为了方便,例如

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

    使用qsort,您只需要对一个整数值进行升序排序,对于标准整数数组,它的格式如下:

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

    如果你喜欢在返回之前进行转换,它只是以下的简写:

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

    (返回比较的差异,避免溢出)

    使用 struct 数组 没有什么不同,您只需取消引用传递给比较函数的指针,直到引用您希望排序的整数值,例如在上述情况下:

    /* 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));
    }
    

    剩下的只是从数据文件中读取数据,丢弃标题行,并将剩余的值存储在 struct 数组 中。既然您知道您有51 状态,您最长的状态名称将适合24-chars(以及9-chars 的格式化整数宽度以使打印更漂亮)您可以使用enum 来为您的程序指定常量。将这些部分放在一起,您可以执行类似于以下的操作:

    #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;
    }
    

    程序只希望文件名作为第一个参数读取数据(如果没有给出参数,它将从stdin 读取)。应用于您的数据,您将获得:

    使用/输出示例

    $ ./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>
    

    查看并考虑使用qsort 方法,而不是滚动您自己的排序例程。 qsort 很有可能会更有效率并且更不容易出错。如果您有任何问题,请告诉我。

    【讨论】:

    • 优雅的解决方案!我添加了一个用于关闭“fp”的编辑。此外,for 循环索引与 C89 不兼容。
    • fclose() 很好,我将添加它,为了完整起见,请使用 C89 的 i。现在应该在 C89 上编译而不会发出警告。
    【解决方案3】:
    #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;
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-07
      • 2014-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多