如何从字符串中提取数字并将它们保存到自己的缓冲区中?

时间:2017-11-28 16:27:19

标签: c string

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

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

我正在寻找一个如何处理问题的指南。我将不胜感激任何帮助。谢谢。

4 个答案:

答案 0 :(得分:3)

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

但是,C还在strtokstrtod中提供了一些方便的工具,可以自动执行部分解析,并为您的数字转换验证其中的一些单调乏味。您也可以选择将数值存储为double(或float使用strtof而不是strtod),然后使用%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没有提供命名的初始值设定项。您还需要在顶部声明计数器变量i以及n

示例使用/输出

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

查看所有答案,总体上有很好的学习方法。如果您还有其他问题,请与我们联系。

答案 1 :(得分:0)

我认为这是一个有点不堪重负的解决方案,而且我写得非常快,只是为了给你一个想法。

#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

答案 2 :(得分:0)

这是一种使用strtok的方法。我认为在Arduino中无法使用strtok,您可以使用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

答案 3 :(得分:0)

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

解决此问题的一种方法是定义一些令牌类型。每种类型都有一组要匹配的字符和一组允许的后续类型。这可以是数据结构或硬编码,但你应该写出来清除你的想法。

  1. 开始期待任何有效的第一类型。
  2. 抓住一个角色
  3. 通过查看有效类型列表,确定它适合的令牌类型。启动该类型的新令牌。
  4. 抓住另一个角色。
  5. 如果字符符合当前标记,则将其添加到字符串中。这可以是原始字符串的副本,也可以是指向符号start和字符数的指针。从4开始重复。
  6. 如果它不合适,则输出令牌。该输出可以是返回值,“out”参数,函数回调或将其添加到数据结构(如列表或数组)。从2开始重复。或者如果你回到呼叫者等待再次呼叫。
  7. 最终您的数据用完或遇到错误并退出。
  8. 每个令牌都是自己的数据结构。它应该包括找到令牌的位置以及令牌所在的东西的类型。当然还有令牌字符串。

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

    您可以使用strspn功能替换步骤4和5。

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