如何在C中浏览任意长度的字符串数组?

时间:2018-06-23 01:45:38

标签: c arrays string pointers gets

了解如何在C中处理直接指针

这里的代码适用于固定数目的项目和固定行长的字符串数组:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXNAMELEN 100
#define MAXLINELEN 100
#define MAXITEMS 1000

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

 FILE * infile, * outfile;
 char name[MAXNAMELEN];
 char line[MAXLINELEN];
 char lines[MAXITEMS][MAXLINELEN];
 int i, items = 0;

 printf("Enter a source filename: ");
 fgets(name, sizeof(name), stdin);
 name[strlen(name)-1] = '\0'; // strip newline
 infile = fopen(name, "r");
 while (fgets(line, sizeof(line), infile)) {
        strcpy(lines[items], line);
        items++;
 }

 qsort(lines, items, MAXLINELEN, strcmp);

 printf("Enter a destination filename: ");
 fgets(name, sizeof(name), stdin);
 name[strlen(name)-1] = '\0'; // strip newline
 outfile = fopen(name, "w");
 for (i=0; i<items; i++) {
    fputs(lines[i], outfile);
 }

 fclose(infile);
 fclose(outfile);
}

问题描述和代码

如果我尝试读取MAXLINELENMAXITEMS内的input.txt文件,则该程序可以正常运行。现在想象一下,我正在逐行读取一个更大的“输入文件”,其中最大行长度可以是任意长度,那么我将不得不使用字符指针(char*)来读取输入。 char* linesptr[MAXITEMS];

这是我的代码,我试图在其中完成以换行符分隔的一行从输入文件读取的操作。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#define MAXNAMELEN 1000
#define MAXLINELEN 1000
#define MAXITEMS 100000

char* linesptr[MAXITEMS];

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

 FILE * infile, * outfile;
 char name[MAXNAMELEN];
 char line[MAXLINELEN];

 int i, items = 0;

 printf("Enter a source filename: ");
 fgets(name, MAXNAMELEN, stdin);
 name[strlen(name)-1] = '\0'; // strip newline
 printf("%s infile \n",name);
 infile = fopen(name, "r");
 while (fgets(line, MAXLINELEN, infile)) {
    int length = strlen(line);
    line[length-1] = '\0';
    linesptr[items] = line; *<- I am writing to the same mem location*
    printf("the input string %d is : %s \n",items,  linesptr[items]);
        items++;
 }

 qsort(linesptr, items, MAXLINELEN, strcmp); 
 printf("Enter a destination filename: ");
 fgets(name, sizeof(name), stdin);
 name[strlen(name)-1] = '\0'; // strip newline
 outfile = fopen(name, "w");
 for (i=0; i<items; i++) {
    fputs(linesptr[i], outfile);
 }

 fclose(infile);
 fclose(outfile);
}

问题

我正在将指针地址复制到数组linesptr的第n个单元中,其中nth是value=items(这是代码linesptr[items] = line;中的参考行)。因此,当您打印最终答案时,我将相同的内存地址引用到名为line的缓冲区中,line处的内存位置将始终指向最新的fgets()。我了解该错误,但不知道如何解决该问题。我希望能帮助您修复代码中的错误。

3 个答案:

答案 0 :(得分:4)

将行复制到动态分配的字符串。

while (fgets(line, MAXLINELEN, infile)) {
    int length = strlen(line);
    if (length > 0 && line[length-1] == '\n') {
        line[length-1] = '\0';
        length--;
    }
    char *linecopy = malloc(length+1);
    strcpy(linecpy, line);
    linesptr[items] = linecpy;
    printf("the input string %d is : %s \n",items,  linesptr[items]);
    items++;
}

如果要处理多于MAXITEMS行,则还应该使用linesptr分配malloc()。当您达到linesptr的当前大小时,可以使用realloc()使其更长。有关详细代码,请参见Read unknown number of lines from stdin, C

有关对字符串指针数组进行排序的正确方法,请参见How to qsort an array of pointers to char in C?

答案 1 :(得分:1)

您要求一个示例,所以这里是:

以下建议的代码:

  1. 出于可读性目的而编写
  2. 检查并处理错误情况
  3. 使用getline()realloc()
  4. 效率不高,因为它为源文件中的每一行调用realloc()
  5. 正确/安全地使用strcspn()删除任何(可能)结尾的换行符
  6. 可以通过将“清理”提取到子函数来简化代码,而不用在遇到错误时重复相同的“清理”代码。
  7. 使用size_t而不是int来索引数组,以避免隐式转换
  8. 尽可能减小变量scope
  9. 将正确的第三个参数传递给qsort()
  10. 正确地为compare()实现qsort()辅助功能

现在,建议的代码:

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


#define MAXNAMELEN 1024

// prototypes
int compare(const void *, const void *);


int main( void )
{
    printf("Enter a source filename: ");
    char name[ MAXNAMELEN ];
    if( !fgets(name, sizeof( name ), stdin) )
    {
        perror( "fgets for input file name failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, fgets for input file name successful

    name[strcspn( name, "\n" ) ] = '\0'; // strip newline
    printf("%s infile \n",name);

    FILE *fp_in = fopen(name, "r");
    if( !fp_in )
    {
        perror( "fopen for input file failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, fopen for input file successful

    char **linesarray = NULL;
    size_t numLines   = 0;

    char   *line      = NULL;
    size_t  lineLen   = 0;

    while( getline( &line, &lineLen, fp_in ) != -1 )
    {
        char ** temp = realloc( linesarray, (numLines+1) * sizeof( char* ) );
        if( !temp )
        {
            perror( "realloc failed" );
            fclose( fp_in );
            for( size_t i = 0; i< numLines; i++ )
            {
                free( linesarray[i]);
            }
            free( linesarray );
            exit( EXIT_FAILURE );
        }

        // implied else, realloc successful

        linesarray = temp;
        linesarray[ numLines ] = line;
        numLines++;

        // prep for next iteration
        line = NULL;
        lineLen = 0;
    }

    free( line );
    fclose( fp_in );

    //puts( "all file read in" );

    qsort( linesarray, numLines, sizeof( char * ), compare );

    //puts( "file sorted" );

    printf("Enter a destination filename: ");

    if( !fgets(name, sizeof(name), stdin) )
    {
        perror( "fgets for output file name failed" );

        for( size_t i = 0; i< numLines; i++ )
        {
            free( linesarray[i]);
        }
        free( linesarray );
        exit( EXIT_FAILURE );
    }

    // implied else, fgets() for output file name successful

    name[strcspn( name, "\n" ) ] = '\0'; // strip newline

    FILE *fp_out = fopen(name, "w");
    if( !fp_out )
    {
        perror( "fopen for output file failed" );

        for( size_t i = 0; i< numLines; i++ )
        {
            free( linesarray[i]);
        }
        free( linesarray );
        exit( EXIT_FAILURE );
    }

    // implied else, fopen for output file successful

    for (size_t i=0; i<numLines; i++)
    {
        if( fputs(linesarray[i], fp_out ) == EOF )
        {
            perror( "fputs failed" );
            fclose( fp_out );

            for( size_t i = 0; i< numLines; i++ )
            {
                free( linesarray[i]);
            }
            free( linesarray );
            exit( EXIT_FAILURE );
        }
    }

    fclose( fp_out );

    for( size_t i = 0; i< numLines; i++ )
    {
        free( linesarray[i]);
    }
    free( linesarray );
}


int compare(const void *ls, const void *rs )
{
    char *leftSide  = *(char**)ls;
    char *rightSide = *(char**)rs;
    return strcmp( leftSide, rightSide );
}

答案 2 :(得分:0)

这是读取文件(大数据),对其进行排序并将其写入文件的完整工作方案:

#include <stdio.h>
#include <string.h> 
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#define MAXNAMELEN 1000
#define MAXLINELEN 5000
#define MAXITEMS 100000

char* linesptr[MAXITEMS];

int compare_function(const void *name1, const void *name2)
{
  const char *name1_ = *(const char **)name1;
  const char *name2_ = *(const char **)name2;
  return strcmp(name1_, name2_);
}

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

 FILE * infile, * outfile;
 char name[MAXNAMELEN];
 char line[MAXLINELEN];

 int i, items = 0;

 printf("Enter a source filename: ");
 fgets(name, MAXNAMELEN, stdin);
 name[strlen(name)-1] = '\0'; // strip newline
 infile = fopen(name, "r");
 while (fgets(line, MAXLINELEN, infile)) {
    int length = strlen(line);
    line[length-1] = '\0';
    char *linecopy = malloc(length);
    strcpy(linecopy, line);
    linesptr[items] = linecopy;
    items++;
 }

 qsort(linesptr, items, sizeof(char *), compare_function);

 printf("Enter a destination filename: ");
 fgets(name, sizeof(name), stdin);
 name[strlen(name)-1] = '\0'; // strip newline
 outfile = fopen(name, "w");
 for (i=0; i<items; i++) {
    fprintf(outfile, "%s\n", linesptr[i]);
 }
 fclose(infile);
 fclose(outfile);
}