2012-02-12 109 views
1

我是新来的C编程语言,并试图通过解决只用C和它的标准libraires从项目欧拉网站的问题得​​到改善。我已经介绍了基本的C基础知识(我认为),函数,指针和一些基本的文件IO,但现在遇到了一些问题。从文件中读取格式字符串到数组用C

这个问题是关于阅读名字的文本文件和计算“名称分数”等等等等,我知道我将使用的算法,并有大部分的程序设置,但只是不知道如何阅读文件正确。

该文件的格式为 “名称一”,“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行上做到这一点?我认为这是否正确?这是甚至可以接受的方式来读取字符串到数组中?

我道歉,如果这是长的和/或不明确的,这是非常晚,我感到非常沮丧。

感谢任何人,每个人的帮助,我很期待最终成为这个网站的活跃成员!

回答

1

实际上有这里有两个基本问题:

  1. 无论扫描输入字符串这里是正确的策略。我认为不是因为它可能在这个任务上工作,而是会遇到更复杂的情况,它很容易中断。
  2. 如何处理45k字符串。

实际上,你不会遇到太多这样大小的字符串,但它不是任何容量的现代计算机都不能轻易处理的东西。只要这是为了学习的目的,然后迭代学习。

最简单的第一种方法是将整个行/文件整合到合适大小的缓冲区中并自行解析。您可以使用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); 
} 
+0

谢谢!这有助于吨..我仍然有一些问题,但。关于上面的#1,为什么这不是一个合理的策略?什么会打破,什么是更复杂的情况?其次,使用'strtok'完美地完成每个名字的分离,但我遇到了剥离引号的问题。在我的函数中,我想我应该明显地将它传递给一个字符串的指针,该字符串是带有引号的名称。我对取消引号的策略以及我应该返回的内容感到困惑。一个新的字符数组的名称?或者编辑字符串本身?谢谢! – 2012-02-13 04:11:04

+0

我觉得我得到它了,我的方法是将令牌指针传递给一个以名字[1]开头的函数,并将每个char复制到一个temp char数组中,直到它到达引号。然后,在我的主'名称[]'字符串数组中为该临时数组的长度分配内存,并将临时字符串复制到主字符串数组中。它运作良好,但这是做这件事的好方法吗?再次感谢 – 2012-02-13 04:55:39

+0

基本上就是这样。因为你认为这将是一个报价,所以传递名称[1]很难看。我开始用这个函数来帮助你,但是用strtok()有很多时髦的小问题,因此解释代码需要更长的时间。这听起来像是大部分,所以我不认为我毁了你的任何东西。这只是一个黑客,所以仍然有改进的空间。 – Duck 2012-02-13 06:06:40

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

不管怎样,当你一次读取一个令牌,还有对输入的大小没有限制。

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

找到第一个双引号,那么第二个,然后在你的名字符串之间读出的字符。然后根据需要针对相关问题处理这些角色。