【问题标题】:How to extract numbers from string and save them into its own buffer?如何从字符串中提取数字并将它们保存到自己的缓冲区中?
【发布时间】:2018-05-12 04:40:17
【问题描述】:

我对一串符号及其值有疑问。我想获取每段字符串的数据并将其保存到自己的缓冲区中。 例如来自这个字符串:

char str = "+TY123-UP65.4-FA545-MTE565-MTD65-MTT230-MPE545-MPD656-MPT345-";

我想在每个符号取代它之后比较字符。首先是表示新数据字符串的“+”号。然后我想在“-”符号出现之前比较前两个或三个值(它表示部分结束),如果它是,例如,数字之前的 TY,则将数字(int 或 float)保存到“TYbuffer”之后" 直到 "-" 符号。然后在“-”之后再次检查哪些字母在前,如果是“UP”,则将其保存到“UPbuffer”中,依此类推。

首先,我像这样遍历字符串:

size_t len = strlen(str);
size_t i;
for (i=0;i<len;i++){ //go through the string
int j=0;
  if (str[j] == '+' && str[j+1]=='T'){ //Check for the letters 
  }
}

这是我将如何做的一个示例,但问题是数字可以更大或更小,因此可以移动位置。我对解决问题有点困惑。我尝试从字符串中提取数字,但后来这个人不知道它来自哪个特定部分。像这样:

char tmp[20];
void loop() {
char *str = "+TY123-UP65.4-FA545-MTE565-MTD65-MTT230-MPE545-MPD656-MPT345-";
char *p = str;
while (*p) {
      if (str == 'T'){
        while (*p) {
          if (isdigit(*p)) { // Upon finding a digit
            long val = strtol(p, &p, 10); // Read a number
            sprintf(tmp, "%1d", val);
            Serial.println(val); // and print it
          } else { 
             p++;
          }
        }

作为参考,我使用的是 Arduino 平台。

我已经设法从字符串中提取了 int/float 值,但我必须处理一些错误。我去掉了开头的“+”号。这是进一步的代码:

#include <stdlib.h>

void setup() {
  Serial.begin(9600);
  char str[] = "TY123-UP65.4-FA545-MTE565-MTD65-MTT230-MPE545-MPD656-MPT345-";
  Serial.write(str);
  analyzeString(str);  
}

void loop() {

}

void analyzeString(char* str) {

  int count = 0;                                  //start the count
  char buff[20];                                  //initialiye the buffer for 20 char

    for(int i = 0; i<strlen(str); i++) {          //start the loop for reading the characters from whole string
      if(str[i] == '-') {                         //start the loop when "-" occurs
        char bufNum[20];                          //buffer for the number in a seperate section
        //char bufNum1[20];
        if (buff[0] == 'T' && buff[1] == 'Y') {   //If the first letter in a buffer is U and second is P 
          for(int j = 2; j<count; j++) {           
            bufNum[j-2] = buff[j];
          }
          bufNum[count-2] = '\0';
          Serial.println();
          int ty = atoi(bufNum);                 //atof(string) for float, atoi(string) for int
          Serial.print(ty);                      //float constant

        } else if (buff[0] == 'U' && buff[1] == 'P'){
          for(int j = 2; j<count; j++) {
            bufNum[j-2] = buff[j];
          }
          bufNum[count-2] = '\0';
          Serial.println(bufNum);
          float up = atof(bufNum);
          Serial.print(up);
        } else if (buff[0] == 'F' && buff[1] == 'A'){
          for(int j = 2; j<count; j++) {
            bufNum[j-2] = buff[j];
          }
          bufNum[count-2] = '\0';
          Serial.println(bufNum);
          int fa = atoi(bufNum);
          Serial.print(fa);
        }


        count =0;

      } else {
          buff[count++] = str[i];
      }
    }
}

错误在输出中,因为它会输出如下部分的下一个值:

TY123-UP65.4-FA545-MTE565-MTD65-MTT230-MPE545-MPD656-MPT345-
12365.4
65.40545
545

我正在寻找有关如何解决问题的指南。我将不胜感激任何帮助。谢谢你。

【问题讨论】:

  • 听起来你需要对字符串进行标记。
  • Serial.println(val);: 这是C吗? Arduino?
  • mnistic 是对的,看看在循环中使用 strtok() 来标记指定字符上的字符串(在本例中为“-”)。这将去掉“-”字符,并为您提供一个指向您实际想要查看的数据的指针。
  • 是的,是的,我注意到它是 Arduino 平台。 @Jean-FrançoisFabre
  • 太好了,我正在研究解决方案,它看起来很有希望。谢谢你。 @K_TrenholmGDC

标签: c string


【解决方案1】:

有多种方法可以从字符串中解析您的部分,然后从每个部分中解析标签和值。首先,简单地沿着字符串向下移动指针,检查'+', '-', 'letter', 'digit' 并采取适当的措施绝对没有错。

但是,C 还在strtokstrtod 中提供了一些方便的工具,可以自动进行部分解析,并为您验证数字转换,从而消除一些乏味。您也可以简单地选择将数值存储为double(或使用strtof 而不是strtodfloat),然后使用%g 处理输出,这将仅在存在小数部分时输出。

Fox 示例,您可以执行以下操作:

#include <stdio.h>
#include <stdlib.h>     /* for strtod  */
#include <string.h>     /* for strlen  */
#include <ctype.h>      /* for isalpha */
#include <errno.h>      /* for errno   */

#define MAXSECT 16      /* if you need a constant, define one */

typedef struct {        /* struct to hold label & value */
    char str[MAXSECT];
    double d;
} section;

int main (void) {

    int n = 0;              /* number of sections */
    char buf[] = "+TY123-UP65.4-FA545-MTE565-MTD65-MTT230-MPE545-MPD656-MPT345-",
        *p = buf,           /* pointer to buf */
        *delim = "+-";      /* delimiters for strtok */
    section sect[MAXSECT] = {{ .str = "" }};    /* array of MAXSECT sections */

    /* tokenize buf splitting on '+' and '-' */
    for (p = strtok(p, delim); p; p = strtok (NULL, delim)) {
        size_t len = strlen (p), idx = 0;   /* length and label index */
        char *ep = p;       /* 'endptr' for strtod */

        if (len + 1 > MAXSECT) {    /* check length of section fits */
            fprintf (stderr, "error: section too long '%zu' chars '%s'.\n",
                    len, p);
            continue;
        }

        while (isalpha (*p))    /* while letters, copy to sect[n].str */
            sect[n].str[idx++] = *p++;
        sect[n].str[idx++] = 0; /* nul-terminate sect[n].str */

        errno = 0;
        sect[n].d = strtod (p, &ep);    /* convert value, store as double */
        if (p != ep)                    /* validate digits converted */
            if (errno) {                /* validate no error on converstion */
                perror ("conversion to number failed");
                continue;
            }
        if (++n == MAXSECT) {           /* increment n, check if array full */
            fprintf (stderr, "sect array full.\n");
            break;
        }
    }

    for (int i = 0; i < n; i++) /* output results, fraction only if present */
        printf ("buf[%3s] : %g\n", sect[i].str, sect[i].d);

    return 0;
}

(注意:如果在旧的 C89 编译器(例如 Win7/VS 10)上编译,您可以初始化 section sect[MAXSECT] = {{0},0};,因为 C89 不提供命名初始化程序。您还需要声明顶部的计数器变量in)

使用/输出示例

$ ./bin/strtok_str_sect
buf[ TY] : 123
buf[ UP] : 65.4
buf[ FA] : 545
buf[MTE] : 565
buf[MTD] : 65
buf[MTT] : 230
buf[MPE] : 545
buf[MPD] : 656
buf[MPT] : 345

查看所有答案,总之有很好的学习。如果您还有其他问题,请告诉我。

【讨论】:

  • 太好了,谢谢你的回答。我设法让它运行起来。
【解决方案2】:

我认为这是一个有点不知所措的解决方案,我写得很快,只是为了给你一个想法。

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

struct data {
    char id[20];
    int i_value;
    float f_value;
};


static int get_ar_size(const char *str)
{
    int count = 0;

    while (*str != '\0') {
        if (*str == '+' || *str == '-')
            count++;
        *str++;
    }
    return (count);
}

static int is_float_string(const char **tmp)
{
    int is_float = 1;
    int is_int = 0;
    for(; *(*tmp) != '\0' && *(*tmp) != '+' && *(*tmp) != '-'; *(*tmp)++) {
        if (*(*tmp) == '.')
            return (is_float);
    }

    return (is_int);
}

static void get_info_from_string(const char *str, int i,
                                 struct data strct_arr[])
{
    int j = 0;
    const char *tmp = NULL;

    /*write two letter ID into id array*/
    while (*str != '\0' && *str != '-' && *str != '+' && isalpha(*str)) {
            strct_arr[i].id[j++] = *str++;
    }

    tmp = str;
    /* then write value for that letter ID */
    while (*tmp != '\0' && *tmp != '-' && *tmp != '+' && isdigit(*tmp)) {
        /* check it is float or it is integer */
        if(is_float_string(&tmp)) {
            strct_arr[i].f_value = atof(&(*str));
            break;
        }
        else {
            strct_arr[i].i_value = atoi(&(*str));
            break;
        }
        *tmp++;
    }
}


int main(void)
{
    const char *str = "+TY123-UP65.4-FA545-MTE565-MTD65-MTT230-MPE545-MPD656-MPT345-";
    int size = 0;
    int index = 0;

    /*count every '+' and '-' it would be our size for struct array*/
    size = get_ar_size(str);

    /* create array of structure which has first letter buf id and its value */
    struct data *strct_arr = malloc(sizeof(struct data) * size + 1);
    if (strct_arr == NULL) {
        perror("Malloc failed: ");
        return EXIT_FAILURE;
    }

    bzero(strct_arr, sizeof(strct_arr));
    for (index = 0; *str != '\0'; *str++) {
        if ((*str == '+' || *str == '-') && (isalpha(*(str+1)))) {
            *str++;
            get_info_from_string(&(*str), index, strct_arr);
            index++;
        }
    }

    index = 0;
    while(index < size) {
        if (strct_arr[index].i_value == 0) {
            printf("ID [%s] float %.1f\n", strct_arr[index].id, strct_arr[index].f_value);
        }
        else
            printf("ID [%s] int %d\n", strct_arr[index].id, strct_arr[iindex].i_value);
        index++;
    }
 return 0;
}

它几乎没有错误检查,你应该小心。想想你能做些什么重写。我从您的字符串中获取的输出:

"+TY123-UP65.4-FA545-MTE565-MTD65-MTT230-MPE545-MPD656-MPT345-";

ID [TY] int 123
ID [UP] float 65.4
ID [FA] int 545
ID [MTE] int 565
ID [MTD] int 65
ID [MTT] int 230
ID [MPE] int 545
ID [MPD] int 656
ID [MPT] int 345

【讨论】:

  • 嘿,谢谢你的努力,我很感激。我会考虑这个解决方案以进行进一步的开发。我还上传了代码的编辑。看看吧:)
【解决方案3】:

这是一种使用strtok 的方法。我认为 strtok 在 Arduino 中不可用,您可以使用 strtok_r 代替。

int main()
{
    char str[] = "+TY123-UP65.4-FA545-MTE565-MTD65-MTT230-MPE545-MPD656-MPT345-";
    //char *context;
    //char *token = strtok_r(str, "+-", &context);
    char *token = strtok(str, "+-");
    while(token)
    {
        for(size_t i = 0, len = strlen(token); i < len; i++)
        {
            if(!isdigit(token[i])) continue;

            //text part, example TY
            char str_name[10];
            strncpy(str_name, token, i);
            str_name[i] = 0;

            //integer part, example 123
            char *temp = token + i;

            if(strstr(temp, "."))
                printf("%s %.2f\n", str_name, strtof(temp, &temp));
            else
                printf("%s %d\n", str_name, strtol(temp, &temp, 10));

            break;
        }
        //token = strtok_r(NULL, "+-", &context);
        token = strtok(NULL, "+-");
    }
    return 0;
}

Run on ideone

【讨论】:

  • 感谢您的发帖。我一定会考虑你发布的内容。我也刚刚以不同的方式编辑了我的代码。看看吧:)
【解决方案4】:

在我看来,您正在构建的是一个词法分析器。您可能会在这个问题上找到一些很好的信息:lexers vs parsers

解决此问题的一种方法是定义一些令牌类型。每种类型都有一组要匹配的字符和一个允许的以下类型列表。这可以是数据结构或硬编码,但您应该将其写出来以理清思路。

  1. 开始期待您有效的第一个类型是什么。
  2. 抓住一个角色
  3. 通过查看有效类型列表找出它适合的令牌类型。启动该类型的新令牌。
  4. 获取另一个角色。
  5. 如果字符适合当前标记,则将其添加到字符串中。这可以是原始字符串的副本,或者您可以有一个指向符号开头和字符数的指针。从 4 开始重复。
  6. 如果不合适,则输出令牌。该输出可以是返回值、“out”参数、函数回调或将其添加到列表或数组等数据结构中。从 2 开始重复。或者,如果您返回呼叫者等待再次呼叫。
  7. 最终您会用完数据或遇到错误并退出。

每个标记都是它自己的数据结构。它应该包括令牌被发现的位置和令牌的类型。当然还有令牌字符串。

您的标记类型看起来像 [+-] 后跟 [A-Z] 后跟 [0-9.],然后回到开头后跟 [+-]。

您可以将步骤 4 和 5 替换为 strspn 函数。

在此之后,接收令牌的代码应该会更容易一些,因为它不会将读取每个字符的低级细节混入其中。

【讨论】:

    猜你喜欢
    • 2018-01-01
    • 1970-01-01
    • 2015-05-14
    • 1970-01-01
    • 1970-01-01
    • 2018-07-30
    • 2013-03-01
    • 1970-01-01
    • 2020-06-15
    相关资源
    最近更新 更多