【问题标题】:Read text file, save all digits into character string读取文本文件,将所有数字保存为字符串
【发布时间】:2014-09-28 20:27:34
【问题描述】:

我正在尝试读取包含字符串“a3rm5t?7!z*&gzt9v”的文本文件,并将所有数字字符放入字符串中,以便稍后转换为整数。

我目前正在尝试通过在读取文件后在缓冲区上使用 sscanf 来执行此操作,然后使用 sprintf 将使用 %u 找到的所有字符保存在名为 str 的字符串中。

但是,每次运行程序时,我在 str 上调用 printf 时返回的整数都不同。我做对了什么,我做错了什么?

当文本文件包含类似“23dog”的字符串并返回 23 时,此代码有效,但当字符串为 23dog2 之类的字符串时无效。

编辑:我现在意识到我应该将数字字符放在一个字符数组中,而不仅仅是一个字符串。

int main(int argc, const char **argv)
{
    int in;
    char buffer[128];
    char *str;
    FILE *input;

    in = open(argv[1], O_RDONLY);
    read(in, buffer, 128);

    unsigned x;
    sscanf(buffer, "%u", &x);
    sprintf(str,"%u\n", x);
    printf("%s\n",str);

    close (in);

    exit(0);
}

【问题讨论】:

  • 如果我没记错的话,带有 %u 的 sprintf 在第一个非数字字符后结束读取输入。您应该在转换为整数之前创建一个纯数字字符串
  • 从我在上一段中得到的输出来看,我不得不同意。有没有办法在 while 循环中仍然使用 sscanf 或其他方法来抵消这种情况?
  • 当你完成它并想检查结果时,你可以使用echo 'a3rm5t?7!z*&gzt9v' | tr -dc [0-9]
  • 您将一个悬空指针传递给sprintf。此外,每次传递的指针都是相同的。您的意思是(部分)覆盖以前的结果吗?最后,对于23dog2,你期待232 还是别的什么?
  • 是的,我希望我在帖子开头提到的字符串中的 23dog2 和 3579 是 232。我的意思是分别读取输入文件中的每个字符并将每个数字附加到字符数组中。

标签: c file input


【解决方案1】:

如果您只是想从输入中过滤掉任何非数字,则无需使用scanf、sprintf 等。只需遍历缓冲区并复制数字字符即可。

以下程序仅适用于从标准输入读取的单行输入,并且仅适用于长度小于 512 个字符的情况,但它应该为您提供正确的想法。

#include <stdio.h>

#define BUFFER_SIZE 512

int
main()
{
  char buffer[BUFFER_SIZE];  /* Here we read into. */
  char digits[BUFFER_SIZE];  /* Here we insert the digits. */
  char * pos;
  size_t i = 0;
  /* Read one line of input (max BUFFER_SIZE - 1 characters). */
  if (!fgets(buffer, BUFFER_SIZE, stdin))
    {
      perror("fgets");
      return 1;
    }
  /* Loop over the (NUL terminated) buffer. */
  for (pos = buffer; *pos; ++pos)
    {
      if (*pos >= '0' && *pos <= '9')
        {
          /* It's a digit: copy it over. */
          digits[i++] = *pos;
        }
    }
  digits[i] = '\0';  /* NUL terminate the string. */
  printf("%s\n", digits);
  return 0;
}

【讨论】:

  • 我试图更好地理解代码,尽管我能够修改它以接受文本文件。看起来我们将 pos 设置为等于缓冲区,然后遍历缓冲区,如果下一个字符是数字,则指向数字数组。括号中的 i++ 不断迭代数字到 1、2、3 等。 null 终止字符串和 fgets 到底是做什么的?
  • @Broaj 几乎完全一样,除了它当然不是“指向数字数组”而是复制到它。 NUL 终止字符串只是意味着在末尾附加一个 NUL 字节,因此printf 知道字符串在哪里结束。 fgets 从文件中读取一行并将其存储到缓冲区中(如果缓冲区足够大,则截断该行)。有关更多信息,请参阅man page。在这里使用fgets 不是必需的,您可以阅读文件,但它对您最方便。
【解决方案2】:

解决此类问题的一个好方法是将整行读入buffer,然后将pointer分配给buffer。然后,您可以使用指针逐步通过缓冲区读取每个字符并适当地对其进行操作。以下是这种方法的一个示例。 getline 用于从文件中读取行(它的优点是为buffer 分配空间并返回读取的字符数)。然后根据getline 返回的缓冲区大小为字符串分配空间。请记住,完成后,您有责任释放getline 分配的内存。

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

int main (int argc, const char **argv)
{
    char *buffer = NULL;    /* forces getline to allocate required space    */
    ssize_t read = 0;       /* number of characters read by getline         */
    size_t n = 0;           /* limit of characters to read, (0 no limit)    */
    char *str = NULL;       /* string to hold digits read from file         */
    char *p = NULL;         /* ptr to use with buffer (could use buffer)    */
    int idx = 0;            /* index for adding digits to str               */
    int number = 0;         /* int to hold number parsed from file          */
    FILE *input;

    /* validate input */
    if (argc < 2) { printf ("Error: insufficient input. Usage: %s filename\n", argv[0]); return 1; }

    /* open and validate file */
    input = fopen(argv[1], "r");
    if (!input) { printf ("Error: failed to open file '%s\n", argv[1]); return 1; }

    /* read line from file with getline */
    if ((read = getline (&buffer, &n, input)) != -1)
    {
        str = malloc (sizeof (char) * read);    /* allocate memory for str      */
        p = buffer;                             /* set pointer to buffer        */
        while (*p)                              /* read each char in buffer     */
        {
            if (*p > 0x2f && *p < 0x3a)         /* if char is digit 0-9         */
            {
                str[idx] = *p;                  /* copy to str at idx           */
                idx++;                          /* increment idx                */
            }
            p++;                                /* increment pointer            */
        }
        str[idx] = 0;                           /* null-terminate str           */

        number = atoi (str);                    /* convert str to int           */

        printf ("\n string : %s number : %d\n\n", buffer, number);

    } else {
        printf ("Error: nothing read from file '%s\n", argv[1]);
        return 1;
    }

    if (input)  fclose (input);                 /* close input file stream          */
    if (buffer) free (buffer);                  /* free memory allocated by getline */
    if (str)    free (str);                     /* free memory allocated to str     */

    return 0;
}

数据文件:

$ cat dat/fwintstr.dat
a3rm5t?7!z*&gzt9v

输出:

$ ./bin/prsint dat/fwintstr.dat

 string : a3rm5t?7!z*&gzt9v
 number : 3579

【讨论】:

  • 干得好。我想避免在我的答案中分配动态内存,因为它增加了额外的复杂性,而 OP 最初也没有它。我必须说,在c &gt; 0x2f &amp;&amp; c &lt; 0x3a、c &gt;= '0' &amp;&amp; c &lt;= '9' 和isdigit(c) 的测试中,我发现第一个最不可读。
  • 另一个最爱是c &gt; '/' &amp;&amp; c &lt; ':' 或c &gt; 47 &amp;&amp; c &lt; 58。目的只是为了避免初始字符转换。我也看到它表示为位移。不过我同意——可读性是关键。它应该正确编码为c &gt;= '0' &amp;&amp; c &lt;= '9'。然而,有时,学习的小金块被洒进来,只是为了这个目的,而不是偶然:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-13
相关资源
最近更新 更多