我是C编程语言的新手,并试图通过仅使用C及其标准库来解决Project Euler网站中的问题。我已经介绍了基本的C基础知识(我认为),函数,指针和一些基本文件IO,但现在遇到了一些问题。
问题是关于读取名字的文本文件并计算“名称得分”等等,我知道我将要使用的算法并且大部分程序设置但是无法弄清楚如何读取文件正确。
该文件采用格式 “名称一”, “Nametwo”, “比利”, “鲍比”, “坦诚” ... 我搜索并搜索并尝试了无数的东西但似乎无法将这些作为单个名称读入字符串数组(我认为这是单独存储它们的正确方法吗?)我尝试使用sscanf / fscanf和%[^ \“,我已经尝试过这些函数和fgets的不同组合,但是我对fgets的理解是每当我调用它时它会得到一个新行,这是一个文本文件,在同一行上有超过45,000个字符。
我不确定我是否遇到了我对scanf函数的误解,或者我对存储字符串数组的误解。就字符串数组而言,我(想)我已经意识到,当我声明一个字符串数组时,它不会为字符串本身分配内存,这是我需要做的事情。但我仍然无法得到任何工作。
以下是我现在尝试阅读的一些代码,这些代码是我从命令行输入的一些名称来测试我的方法。
此代码用于输入缓冲区大小(100)的任何字符串:
int main(void)
{
int i;
char input[100];
char* names[10];
printf("\nEnter up to 10 names\nEnter an empty string to terminate input: \n");
for(int i = 0; i < 10; i++)
{
int length = 0;
printf("%d: ", i);
fgets(input, 100, stdin);
length = (int)strlen(input);
input[length-1] = 0; // Delete newline character
length--;
if(length < 1)
{
break;
}
names[i] = malloc(length+1);
assert(names[i] != NULL);
strcpy(names[i], input);
}
}
但是,我根本无法在格式化的字符串中读取这项工作。
请告诉我如何用格式阅读。我之前在输入缓冲区上使用了sscanf并且工作正常,但是我觉得我不能在45000+ char线上做到这一点?我假设这是正确的吗?这甚至是将字符串读入数组的可接受方式吗?
如果这很长和/或不清楚,我很抱歉,已经很晚了,我非常沮丧。
感谢任何人和大家的帮助,我期待着最终成为这个网站的活跃会员!
答案 0 :(得分:1)
这里有两个基本问题:
实际上你不会碰到太多这么大的字符串,但任何容量的现代电脑都无法轻易处理。只要这是为了学习目的,那么迭代学习。
最简单的第一种方法是将整个行/文件fread()
放入适当大小的缓冲区并自行解析。您可以使用strtok()
拆分逗号分隔的标记,然后将标记传递给剥离引号并返回单词的函数。将单词添加到数组中。
对于第二遍,你可以取消strtok()
,只需通过遍历缓冲区并自己分解逗号标记来自己解析字符串。
最后但并非最不重要的是,您可以编写一个版本,将较小的文件块读入较小的缓冲区并进行解析。这增加了处理多个读取和管理缓冲区的复杂性,以便在缓冲区结束时考虑半读取令牌等等。
在任何情况下,将问题分解为块并学习每个细化。
修改强>
#define MAX_STRINGS 5000
#define MAX_NAME_LENGTH 30
char* stripQuotes(char *str, char *newstr)
{
char *temp = newstr;
while (*str)
{
if (*str != '"')
{
*temp = *str;
temp++;
}
str++;
}
return(newstr);
}
int main(int argc, char *argv[])
{
char fakeline[] = "\"Nameone\",\"Nametwo\",\"billy\",\"bobby\",\"frank\"";
char *token;
char namebuffer[MAX_NAME_LENGTH] = {'\0'};
char *name;
int index = 0;
char nameArray[MAX_STRINGS][MAX_NAME_LENGTH];
token = strtok(fakeline, ",");
if (token)
{
name = stripQuotes(token, namebuffer);
strcpy(nameArray[index++], name);
}
while (token != NULL)
{
token = strtok(NULL, ",");
if (token)
{
memset(namebuffer, '\0', sizeof(namebuffer));
name = stripQuotes(token, namebuffer);
strcpy(nameArray[index++], name);
}
}
return(0);
}
答案 1 :(得分:0)
fscanf("%s", input)
一次读取一个标记(一个由空格包围的字符串)。您可以扫描输入,直到遇到特定的“输入结束”字符串,例如“!”,或者您可以等待文件结束信号,这可以通过按“Ctrl + D”来实现Unix控制台或在Windows控制台上按“Ctrl + Z”。
第一个选项:
fscanf("%s", input);
if (input[0] == '!') {
break;
}
// Put input on the array...
第二个选项:
result = fscanf("%s", input);
if (result == EOF) {
break;
}
// Put input on the array...
无论哪种方式,当您一次读取一个令牌时,输入的大小没有限制。
答案 2 :(得分:0)
为什么不在巨型字符串中搜索引号字符呢?像这样:
#include <stdio.h>
#include <string.h>
int main(void)
{
char mydata[] = "\"John\",\"Smith\",\"Foo\",\"Bar\"";
char namebuffer[20];
unsigned int i, j;
int begin = 1;
unsigned int beginName, endName;
for (i = 0; i < sizeof(mydata); i++)
{
if (mydata[i] == '"')
{
if (begin)
{
beginName = i;
}
else
{
endName = i;
for (j = beginName + 1; j < endName; j++)
{
namebuffer[j-beginName-1] = mydata[j];
}
namebuffer[endName-beginName-1] = '\0';
printf("%s\n", namebuffer);
}
begin = !begin;
}
}
}
您找到第一个双引号,然后是第二个引号,然后将您之间的字符读出到您的名字字符串中。然后根据需要处理这些字符。