将名称从文件复制到char *数组的功能

时间:2015-05-01 12:39:27

标签: c arrays pointers char

作为我的程序的一部分,我需要将文件中的单个名称复制到定义为char *Names[NumOfNames]的数组中,其中NumOfNames是一个整数,用于保存文件中的名称总数。我是指向数组的新手,这似乎是我的麻烦所在。

该文件采用以下格式编写:

  

JohnFrankJamesPeter

(即每个名字都以大写字母开头,名称之间没有空格)

到目前为止,这是我未完成的功能:

void LoadNamesIntoArray()
{
    char ch;
    int NumOfNames = 0;

    FILE *fpn = fopen(NamesFilePath, "r+");

    if (fpn == NULL)
    {
        printf("Cannot open %s for reading. \n", NamesFilePath);
        printf("Set up Names file at %s and restart. \n", NamesFilePath);
        perror("Error opening Names file");
    }

    do{
        ch = fgetc(fpn);
        if(isupper(ch)){
            NumOfNames++;
        }
    }while(ch != EOF);

    char *Names[NumOfNames];
    ...
    ...
    ...
}

我尝试了几种方法,使用fgets函数和islower()函数将每个名称复制到数组的每个元素中,以识别何时移动到Names数组的下一个元素。

我希望数组以这样的方式

printf("%s", Names[0])

会打印“John”等等。这可能吗?对于我出错的地方有任何帮助或帮助将不胜感激。

修改

我现在正在努力将每个名称复制到一个临时数组中,然后复制到char *Names[NumOfNames}的每个元素中。但是,为了测试文件是否正确地复制到临时数组中,我尝试将其打印出来,但是打印不正确。这是代码块:

do{
    ch = fgetc(fpn);
    TempName[i] = ch;
    i++;
}while(ch != EOF);

for(i = 0; i<15; i++){
    printf("%c", TempName[i]);
}

我知道这会打印出前15个字符而不是单个名字,但我的问题是它打印的是所有奇怪的符号而不是实际的字母。

4 个答案:

答案 0 :(得分:2)

如果你知道每个名字的最大尺寸是15,你可以这样做:

char buf[15];
int i = 0;
do {
    if ((ch = fgetc(fpn)) == EOF) break;
    buf[i++] = ch;
    if (isupper(ch)) {
        Names[NumOfNames] = malloc(i * sizeof(char));
        memcpy(Names[NumOfNames]), buf, i -1);
        i = 0;
        NumOfNames++;
    }
} while(ch != EOF);

否则你必须使用realloc而不是buf [15]。

答案 1 :(得分:2)

良好的第一步,找到名字的数量。

添加确定最大长度。

size_t MaxLength = 0;
size_t CurentLength = 0;
while ((ch = fgetc(fpn)) != EOF) {
  if(isupper(ch)){
    CurrentLength = 0; 
    NumOfNames++;
  }
  CurentLength++;
  if (CurrentLength > MaxLength) {
    MaxLength = CurrentLength; 
  }
} 

回放文件,分配缓冲区。

rewind(fpn);
char *Buffer = malloc(MaxLength + 1);
// +1 here to deal with files that do not begin with  A-Z
char **Names = malloc((NumOfNames + 1) * sizeof *Names);

然后再次读取名称并使用strdup()为每个名称分配空间。 strdup()不是标准C,但在POSIX中是标准的,因此通常可用 - 见下文。

size_t i = 0;
size_t name_index = 0;
for(;;)  {
  ch = fgetc(fpn);
  if (ch == EOF || isupper(ch)) {
    buffer[i] = '\0';
    if (i > 0) Names[name_index++] = strdup(buffer);
    if (ch == EOF) break;
    i = 0;
  }
  buffer[i++] = ch;
}

[编辑]

示例strdup()实施。

char *strdup(const char *str) {
  size_t len = strlen(str) + 1;
  char *copy = malloc(len);
  if (copy) {
    memcpy(copy, str, len);
  }
  return copy;
}

答案 2 :(得分:0)

首先它只是奇怪的符号。字符串是名称和nul字节'\ 0'的字母,不要与NULL混淆。这意味着John的名字需要5个字母位置[0,3]用于字母,'\ 0'用于4。

在任何时候你都没有用nul字节标记数组的末尾。

完成这个问题的最好方法是测量文件,创建数组,回放文件然后处理它,将字母放入每个末尾标记'\ 0'的数组中。

打印字符串前15个的最佳方法是

printf("%15s",tmpname) ;

答案 3 :(得分:0)

读取每个字符并检查它是否为上限允许拆分名称。快速方法看起来像:

#include <stdio.h>

#define MAXNM 256
#define MAXCH 32

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

    char names[MAXNM][MAXCH] = {{0}};
    FILE *fp = NULL;
    size_t idx = 0;
    size_t i = 0;
    int c = 0;

    if (argc < 2 ) {
        fprintf (stderr, "error: insufficient input, usage: %s filename\n", argv[0]);
        return 1;
    }

    if (!(fp = fopen (argv[1], "r"))) {
        fprintf (stderr, "error: file open failed '%s'.\n", argv[i]);
        return 1;
    }

    /* read each char in file */
    for (;;) {
        while ((c = getc(fp)) != '\n' && c != EOF)
        {
            if (c >= 'A' && c <= 'Z') {     /* if c is Cap,         */
                if (i) names[idx++][i] = 0; /* null-term & new name */
                i = 0;                      /* reset i = 0          */
                if (idx == MAXNM) break;    /* if MAXNM, break      */
            }
            names[idx][i++] = c;            /* add c to name[idx]   */
            if (i == MAXCH) {               /* if MAXCH             */
                names[idx++][i-1] = 0;      /* null-term/trucate    */
                while ((c = getc(fp)) < 'A' || c > 'Z') /* next Cap */
                    {}
                ungetc (c, fp);             /* put it back          */
                i = 0;
                continue;
            }
        }
        if (c == EOF) {
            names[idx++][i] = 0;            /* null-terminate last  */
            break;
        }
        if (idx >= MAXNM) break;
    }

    fclose (fp);                            /* close input file     */

    for (c = 0; c < idx; c++)               /* output names         */
        printf ("names[%2d] : %s\n", c, names[c]);

    return 0;
}

<强>输出

$ ./bin/split_on_caps dat/Capnames.txt
names[ 0] : John
names[ 1] : Frank
names[ 2] : James
names[ 3] : Peter

关于你的问题,唯一不清楚的是你是否愿意为每个字符串动态分配或者静态声明是否足够。您可以清楚地分配一定数量的指针(例如char *names[MAXNM] = calloc (MAXNM, sizeof *names[0]);),或者您可以静态分配数组,如上所述。唯一的区别是你有能力在你用完的时候重新分配更多的名字指针。