函数将文件名复制到char*数组中
作为程序的一部分,我需要将文件中的单个名称复制到定义为函数将文件名复制到char*数组中,c,arrays,pointers,char,C,Arrays,Pointers,Char,作为程序的一部分,我需要将文件中的单个名称复制到定义为char*names[numoffnames]的数组中,其中numoffnames是一个整数,用于保存文件中的名称总数。我不熟悉数组指针,这似乎就是我的问题所在 该文件以以下格式编写: 约翰弗兰克贾梅斯佩特 (即每个名称以大写字母开头,名称之间没有空格) 以下是我目前尚未完成的功能: void LoadNamesIntoArray() { char ch; int NumOfNames = 0; FILE *fpn
char*names[numoffnames]
的数组中,其中numoffnames
是一个整数,用于保存文件中的名称总数。我不熟悉数组指针,这似乎就是我的问题所在
该文件以以下格式编写:
约翰弗兰克贾梅斯佩特
(即每个名称以大写字母开头,名称之间没有空格)
以下是我目前尚未完成的功能:
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()
函数识别何时移动到名称数组的下一个元素
我希望阵列的方式能够
printf("%s", Names[0])
将打印“John”等字样。这可能吗?任何关于我哪里出了问题的帮助都将不胜感激
编辑
我现在正在尝试将每个名称复制到一个临时数组中,然后复制到char*Names[numoffnames}
的每个元素中。但是,为了测试文件是否正确复制到临时数组中,我尝试将其打印出来,但打印不正确。下面是代码块:
do{
ch = fgetc(fpn);
TempName[i] = ch;
i++;
}while(ch != EOF);
for(i = 0; i<15; i++){
printf("%c", TempName[i]);
}
do{
ch=fgetc(fpn);
TempName[i]=ch;
i++;
}while(ch!=EOF);
对于(i=0;i如果您知道每个名称的最大大小为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]。首先,它只是奇怪的符号。字符串是名称的字母和nul字节'\0',不要与NULL混淆。这意味着名称John需要5个字符位置[0,3]表示字母,而'\0'表示4
在任何情况下都不能在数组的末尾加盖nul字节
解决整个问题的最佳方法是测量文件,创建数组,倒带文件,然后处理它,将字母放入数组中,在每个字符的末尾打上“\0”
打印字符串的前15个字符的最佳方法是
printf("%15s",tmpname) ;
好的第一步,找到名字的数目
加上最大长度的确定
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()
为每个名称分配空间。这不是标准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;
}
读取每个字符并检查其是否为cap允许拆分名称。快速方法如下所示:
#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;
}
关于您的问题,唯一不清楚的是您是喜欢为每个字符串动态分配还是静态声明就足够了。您可以清楚地分配一定数量的指针(例如char*names[MAXNM]=calloc(MAXNM,sizeof*names[0]);
)或者您可以如上所述静态分配数组。唯一的区别是您可以在用完时重新分配更多的名称指针。ch
应具有typeint
。否则您无法将其与EOF
进行比较。原因是fgetc()
返回int
而不是char
是专门为EOF
值获取一些额外的范围,该值与有效的char
值不重叠。请注意char*Names[n]为您提供n个指向字符串的指针,但它不会同时为您提供n个字符串。要做到这一点,您必须单独分配字符串。因此,您可能应该一次读取一个字符到缓冲区,直到到达名称的结尾,然后零终止该缓冲区,然后使用名称[count++]=strdup(buffer)分配/复制它。在循环中重复,直到完成。如果您知道一个名称的最大长度,这会更容易。您知道吗?文件中有很多名称,所以我不知道,它们肯定都少于15个。从快速扫描来看,我能看到的最长长度是12个。名称列表末尾有换行吗?printf(“%15s”),“123456789012134567890”);
-->
123456789001234567890。%15s
中的15确保输出至少是15个char
宽,根据需要填充”
。是的,你说得对,我的错。唯一的方法是这两行。string[15]='\0';printf(“%s”,string);.15s而不是%15s?不同意”唯一的方法是使用这个2行“,”并同意@jarmodprintf(“%.15s”,“123456789001234567890”);
-->1234567890123345。“.”的意思是否准确。你能将它与诸如“.3%d”Names[numoffnames]=malloc(i*sizeof(char));…Names[numoffnames][i]='\0';
这样的数字一起使用吗strncpy()
中的[NumOfNames]`-1`可疑。由于代码知道数组长度,建议memcpy()
。您可能会注意到,现在您可以,可能应该,将do…while
循环替换为while((ch=fgetc(fpn))!=EOF)
,这是循环的正常形式。循环结束后,您需要处理姓氏的尾部-添加尾随的null并增加计数器;这也是一个问题混乱的地方。此外,strncpy()
仅在其第一个参数为null时返回null;if
和perror()
不是必需的。还要注意perror()
不会退出;您的代码将继续运行。@chux这是有道理的。@JonathanLeffler是的,只是保留了do…while,因为OP就是这样提出这个问题的。不要忘记,如果文件大小为GB,或者实际上连接到终端、管道或其他设备,那么读取两次文件可能会非常昂贵,甚至不可能ocket。对于实际的学生练习,大小很少大到引起麻烦。@Jonathan Leffler同意如果倒带失败,不可能的部分。幸运的是,OP的代码需要处理“na”
$ ./bin/split_on_caps dat/Capnames.txt
names[ 0] : John
names[ 1] : Frank
names[ 2] : James
names[ 3] : Peter