【问题标题】:Filtering String to a legit string in C将字符串过滤为C中的合法字符串
【发布时间】:2017-05-05 08:30:18
【问题描述】:

我正在用 c 编写一个程序。传入的字符串是这样的*H1W000500,这是一个合法的字符串,我将*H1W之后的字符串内容,即000500复制到一个整数类型。

但是如果字符串不合法,我想过滤这个字符串。例如*H1W.....*H1W~@#$,如果字符串不合法,请勿复制内容并跳过。仅当字符串如上所述合法时才复制内容。

这就是我正在做的事情,但只要存在不相关的字符串,它就会复制零值,这是不可取的。

 char ReceivedData[50];
 unsigned int Head1Weight;

 p = strstr(ReceivedData, "*H1W");
 if(p)
 {
    Head1Weight = strtoul(p+4,&ptr,10);
 }

【问题讨论】:

  • 所以...之后真的检查ptr?我的意思是,这就是它的用途。
  • 还要注意strtoul会转换成unsigned long,所以Head1Weight应该声明为这种类型。
  • 查看 melpomene 的评论:if(ptr - p == 10) { /* success */ }(如果您需要正好 6 位数字)。此外,您可能希望字符串以空格结尾:if(*ptr == 0 || isspace(*ptr)) { /* success */ }
  • 什么?我不太明白:可能你的意思是:if (sscanf(ptr,"*H1W%d",&intvar)==1) /*OK*/ else /*NOT OK*/
  • 一个有效的字符串必须正好有 6 位数字吗?如果不能,什么是允许的?整数可以有符号,前导空格吗? Head1Weight 是否必须适合 unsigned 范围,即使 `unsigned 是 16 位?

标签: c string int


【解决方案1】:

您很接近,但您对strstr 的使用可以更好地表达为strncmp 来比较receiveddata 的第一个4 字符。 (如果您的目标字符串存在于receiveddata 的中间,那么strstr 就可以了)您还需要对strtoul 转换提供错误检查。将这些部分放在一起,您可以执行以下操作(注意:这是针对单个值显示的,在循环中,将 return 更改为 continue,如 cmets 中所述)

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

/* declare constants, avoid magic number use in code */
enum { PRE = 4, BASE = 10, MAX = 50 };

int main (void) {

    char receiveddata[MAX] = "*H1W000500", *p = NULL;
    unsigned long head1weight;

    if (strncmp (receiveddata, "*H1W", PRE) != 0)   /* cmp 4 chars */
        return 1;   /* you would continue here */

    if (strlen (receiveddata) <= PRE)               /* more chars exist? */
        return 1;   /* you would continue here */

    errno = 0;  /* set errno to known value */
    head1weight = (unsigned)strtoul (&receiveddata[PRE], &p, BASE);

    /* check for error conversions on conversion */
    if ((errno == ERANGE && (head1weight == ULONG_MAX)) || 
        (errno != 0 && head1weight == 0)) {
        perror ("strtoul");
        return 1;   /* you would continue here */
    }

    if (&receiveddata[PRE] == p) {  /* check if chars converted */
        fprintf (stderr, "No digits were found\n");
        return 1;   /* you would continue here */
    }

    printf ("head1weight : %lu\n", head1weight);

    return 0;
}

使用/输出示例

$ ./bin/parsetounsigned
head1weight : 500

查看一下,如果您还有其他问题,请告诉我。

(注意:C 一般避免使用MixedCasecamelCase 变量名,支持全部小写,保留全部大写以用于常量和宏。它是样式,所以完全向上给你...)

【讨论】:

  • 注意:这通过了"*H1W 00500""*H1W-00500""*H1W+00500"。您认为(errno == ERANGE &amp;&amp; (head1weight == ULONG_MAX))(errno == ERANGE) 的价值是什么?或者为什么不简单地if (errno) {
  • 我明白了,但是检查是根据 man 3 strtoul 描述的“除非原始(非否定)值会溢出;在后者在这种情况下,strtoul() 返回 ULONG_MAX 并将 errno 设置为 ERANGE。”,所以我认为最好包含所描述的检查?错了吗?
  • IMO,包含head1weight == ULONG_MAX 测试并没有错,也没有更好的选择,但不必要地更复杂。我在寻找你的理由。 IAC,以某种方式展示对errno 的测试很好。
  • 顺便说一句:"*H1W000500x" 也通过了此代码,因为没有检测到额外的文本。在这一点上,OP 的目标尚不清楚。
  • 观点很好——简短、简洁、有效的验证,不太容易演变成无效的膨胀。如果没有基于ERANGE 检查的独特处理(这里没有),则仅errno 检查的参数是简洁有效的选择。很好的讨论,也祝你周末愉快:)
【解决方案2】:

来自strtoul 上的 Linux 手册页。

如果根本没有数字,strtoul() 将 nptr 的原始值存储在 *endptr 中(并返回 0)。

因此,如果在strtoul 之后,ptr 与起始指针相同,则说明没有合法字符。

char* ptr;

unsigned long Head1Weight = strtoul(p + 4, &ptr, 10);
if (ptr == p + 4)
{
    // There were no digits
}
else if (strlen(ptr) > 0)
{
    // There were characters in the string after the end of the number
}

【讨论】:

    【解决方案3】:

    OP 的p = strstr(ReceivedData, "*H1W"); if(p) { 是不够的,因为它通过了strstr("abcd12*H1W", "*H1W"),尽管这是一个开始。


    OP 的验证目标不够具体。 "*H1W 之后的字符串内容,即 000500 为整数类型。"

    1. “+123”、“-123”、“123”计算为整数,它们有效吗?

    2. “123”可以计算为整数,有效吗?

    3. 示例暗示数字部分应该正好是 6 位十进制数字,但这是不确定的。

    4. 示例代码使用unsigned,可以是 16 位吗?只有“000000”到“065535”有效吗?

    5. “-123”通过strtoul()转换成功,对这个目标有效吗?

    6. 是否应该通过“*H1W000500xyz”?是否允许或忽略额外的文本?


    这在编写代码时常见,因为规范最初存在解释问题,然后趋于发展。

    代码应该允许进化。


    让我们从*H1W 开始,紧跟6 位十进制数字sscanf()。下面的代码使用"%n" 记录检查数字后的扫描位置。如果PREFIX 包含%,这种方法需要额外的工作。

      // PREFIX should not contain %
      #define PREFIX "*H1W"
      #define DIGIT_FMT "%*[0-9]"
      #define VALID_LENGTH 10
    
      char ReceivedData[50];
      unsigned long Head1Weight = 0;
    
      int n = 0;
      sscanf(ReceivedData, PREFIX DIGIT_FMT "%n", &n);
    
      if (n == VALID_LENGTH && ReceivedData[VALID_LENGTH] == '\0') {
        Head1Weight = strtoul(ReceivedData + sizeof PREFIX - 1, NULL, 10);
      }
    

    【讨论】:

      猜你喜欢
      • 2010-11-20
      • 2011-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多