【问题标题】:strtod with limited string length字符串长度有限的 strtod
【发布时间】:2013-05-01 00:58:12
【问题描述】:

如果我想将 char 数组中的前 3 个字符解析为双精度字符,忽略后面的字符,我真的需要这样做吗?

int main() { const char a[] = "1.23"; char *b = malloc(sizeof(char) * 4); memcpy(b, a, sizeof(char) * 3); b[3] = '\0'; printf("%f\n", strtod(b, NULL)); // 打印 1.20000,这是我想要的 免费(乙); }

难道没有像strtod 这样的函数可以让您指定它应该搜索数字的最大字符串长度吗?

编辑:我希望它打印1.2(它目前正在打印),1.23

【问题讨论】:

    标签: c strtod


    【解决方案1】:

    虽然strtod() 不允许您限制字符串长度,但您可以将sscanf()最大字段宽度 一起使用,并可选择检查消耗的字符数,如下所示:

    #include <stdio.h>
    
    double parseDouble(const char *str){
        double val = 0;
        int numCharsRead;
    
        // Handle errors by setting or returning an error flag.
        if(sscanf(str, "%3lf%n", &val, &numCharsRead) != 1){
            puts("Failed to parse double!");
        }
        else if(numCharsRead != 3){
            puts("Read less than three characters!");
        }
    
        return val;
    }
    
    int main(){
        printf("%lf\n", parseDouble("1.3")); // 1.300000
        printf("%lf\n", parseDouble("1.5999")); // 1.500000
        printf("%lf\n", parseDouble(".391")); // 0.390000
        printf("%lf\n", parseDouble(".3")); // Read less than three characters!\n0.300000
        return 0;
    }
    

    sscanf(str, "%3lf%n", &amp;val, &amp;numCharsRead 是重要部分:您指定最大宽度为 3,这意味着 sscanf() 将读取该特定字段的最多 3 个字符,并存储字符数在 numCharsRead 中的解析结束时消耗。如果您关心每次准确读取 3 个字符,则可以检查该值;如果你对 3 个或更少没问题,你可以使用sscanf(str, "%3lf", &amp;val)。作为参考,这里是宽度说明符的文档:

    一个可选的十进制整数,它指定最大字段宽度。 当达到此最大值或 找到不匹配的字符,以先发生者为准。大多数转化 丢弃最初的空白字符(例外情况如下所述), 并且这些丢弃的字符不计入最大字段宽度。 字符串输入转换存储一个终止空字节 ('\0') 以标记结束 输入;最大字段宽度不包括此终止符。

    【讨论】:

    • 不错,除了 2 分。 1) 使用str == " 1.3",--> numCharsRead == 4val == 123"%3lf%n" 中的 3 将扫描并在转换中使用的章程数限制为最多 3 个。 %n 报告扫描的数字,是否用于转换。 2) val 未在失败路径上定义。先试试printf("%lf\n", parseDouble("xyz"));
    • 是的,第一个很好。我没有提出来,但它在文档中引起了我的注意;唯一的解决方案可能是手动跳过字符串中的任何空格。至于val 未定义,你是对的,但这只是一个简单的例子。实际上,您可能希望以不同的方式构造该函数,如果解析失败,则设置或返回错误标志。
    • 并不是说确定 val 计数对 OP 的目标很重要,但代码可以使用 " %n%3lf%n", &amp;before, &amp;val, &amp;after
    【解决方案2】:

    如果您总是想只考虑给定字符串的前三个字符,可以使用以下代码:

    #include <stdio.h>
    #include <string.h>
    
    double parse_double(const char *str) {
      char *tmp = 0;
      double result = 0;
    
      asprintf(&tmp, "%.3s", str);
      result = strtod(tmp, 0);
      free(tmp);
    
      return result;
    }
    
    int main(void) {
      printf("%f\n", parse_double("1.23")); // 1.2
      printf("%f\n", parse_double("1234")); // 123
      printf("%f\n", parse_double("0.09")); // 0.0
    
      return 0;
    }
    

    【讨论】:

    • 我没有想要的精度,我希望它受到字符串大小的限制,所以"1.23" =&gt; 1.2, "1234" =&gt; 123.0 & "0.09" =&gt; 0.0
    • @alk 您获取字符串的前三个 字符"1234" =&gt; "123",然后将其转换为双精度 "123" =&gt; 123.0
    • @Tyilo:好的,我明白了...... - 无论这背后的想法是什么,您可能希望看到我的答案,以获得可能的解决方案。
    • @Tyilo:欢迎,但是你确定你接受了正确的答案... ;->>?
    • @Tyilo,这只是你在问题代码中显示的一种迂回方式,除了asprintf() 不是很便携,memcpy() 更适合只复制字节.正确的方法是sscanf(),我添加了一个关于它的答案。
    【解决方案3】:

    strtod的签名是这样的

       double strtod(const char *nptr, char **endptr);
    

    该函数将返回nptr 指向的字符串的初始部分。如果endptr 不是NULL,则指向转换中使用的最后一个字符之后的字符的指针存储在endptr 引用的位置。

    所以它不允许您指定需要转换的字符数。因此,您必须自己修改输入并将其传递给strtod

    【讨论】:

    • 我已经知道了。我在问是否有另一个功能,它提供了我想要的功能。
    【解决方案4】:

    不,标准库中没有这样的功能。

    但是自己动手很有趣:

    /*
     * Same as strtod() but only takes the first n characters into account.
     * Additionally returns 0. and sets errno to EINVAL if 'nptr' is NULL.
     */
    double strntod(const char *nptr, char **endptr, size_t n)
    {
      double result;
    
      /* perform input validation */
      if (!nptr)
      {
        errno = EINVAL;
    
        result = 0.;
        if (endptr)
        {
          *endptr = nptr;
        }
    
        goto lblExit;
      }
    
      if (strlen(nptr) <= n)
      {
        /* Nothing to truncate: fall back to standard 'strtod()' */        
        result = strtod(nptr, endptr);
      }
      else
      {
        /* create working copy of string */
        char * ptmp = strdup(nptr);
    
        /* Test whether 'strdup()' failed */
        if (!ptmp)
        {
          result = 0.;
          if (endptr)
          {
            *endptr = nptr;
          }
    
          goto lblExit;
        }        
    
        /* truncate working copy to n characters */
        ptmp[n] = '\0'; 
    
        /* do original 'strtod()' on truncated working copy */
        result = strtod(ptmp, endptr);
    
        /* adjust '*endptr' to point to original character array, but to working copy */
        if (endptr)
        {
          *endptr = nptr + (*endptr - ptmp); 
        }
    
        /* free working copy */
        free(ptmp);
      }
    
      lblExit:
    
      return result;
    }
    

    【讨论】:

    • 反引号有什么作用?
    • @JL2210 哪个反引号?
    • @JL2210:啊。他们是一个错字。感谢您解决此问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多