Перейти к содержанию
    

STM32 и либа string.h (strstr)

Прошу помощи, целый день сижу и не могу разрешить одну проблемку. А именно в парсинге строки.

Хочу принятую строку данных (по USB VirtaulComPort) разбить на две, если в ней есть числовая часть (пример: "Read13" (команда+число), надо число выделить и переложить в другой массив (так потом легче с отдельным "числовым" массивом работать - преобразовывать уже в цифровые константы. Недавно узнал и начал активно использовать библиотеку string.h. Очень сильно помогает в работе со строками при терминальном общении с МК. Но тут зашел в тупик :help: Код:

// Тут описано тело функции, в которую мы попадаем при вводе данных с терминала

// Массив строк, состоящих из одних цифр от 0 до 9
char *Numbers[] = {"1","2","3","4","5","6","7","8","9","0"};
// вычисляем длину принятого массива данных по USB (VCP)
int16_t len = strlen((const char*)UserRxBufferFS);
uint8_t pointer = 0;	// индекс массива, первого обнаружения числа

// Если что-то пришло в порт
if(len > 0)
{
  // копирование данных из буфера RX в буфер TX до конца всей строки (len), с символом конца строки (нул)
  snprintf((char *)UserTxBufferFS, len+1, "%s", (const char*)UserRxBufferFS);
  // очистим массив
  memset(Array_of_Numbers, 0, sizeof(Array_of_Numbers));

  // Найдем число из массива данных
  // Перебираем наш массив данных в поисках совпадений с числами из массива Numbers
  for (int i = 0; i < sizeof(Numbers)/sizeof(Numbers[0]); i++)
  {
    // если нашли совпадение с одним из чисел (0-9)
    if (strstr((char*)UserTxBufferFS, Numbers[i]) != NULL)
    {
      // находим номер индекса с которого начинаются числа в общем массиве данных
      pointer = strstr((char*)UserTxBufferFS, Numbers[i])-(char*)UserTxBufferFS;
      // копируем из общего массива в новый, числа начиная с индекса, в котором была обнаружена первая из 10 цифр
      for (int j = 0; j < len-1; j++)
      {
        Array_of_Numbers[j] = UserTxBufferFS[pointer+j];
      }
      break;	// выходим из цикла поиска чисел, если нашли первое совпадение
    }
  }
  // выводим ново-созданный массив в порт
  HAL_Delay(1);
  strcat((char *)Array_of_Numbers, (char*)" - new mass\n\r");
  CDC_Transmit_FS((uint8_t*)Array_of_Numbers, strlen((const char*)Array_of_Numbers));
  sprintf((char *)Array_of_Numbers, "Point: %d\n\r", pointer);
  HAL_Delay(1);
  CDC_Transmit_FS((uint8_t*)Array_of_Numbers, strlen((const char*)Array_of_Numbers));
}

Получается очень странное. Код работает, если вводить числа от 0 до 9, высчитывает индекс и перезаписывает в новый массив, вроде все хорошо.

НО если вводить двузначные числа, в которых второе число меньше первого, то в новый массив записывается только это последнее число, игнорируя "первое вхождение".

На примере из терминала сверху вниз: ввожу "10" - ОК; "100" - ОК; "11" - ОК; "12" - ОК; "13" - ОК; "ddff11" - ОК (первое вхождение 4); "ddff44" - OK; а вот уже:"ddff43" - считает первое вхождение только с числа "3" и обрезает "4". Тоже самое с числами "21, 31, 32, 43, 42, 41, 54, 53, 52, 51 и т.д.). Конечно код такой себе, но тут уже дело принципа, что и почему не так????? Думал, может цикл два раза выполняется, но нет он выполняется как надо, оператор break вовремя его прерывает. Почему происходит такая лабуда при вводе последовательно чисел. Буду рад помощи :)) Может надо какую то другую функцию из string.h использовать, но на ум приходит только strchr, но не уверен, что он чем то кардинальным отличается от strstr. (на Unknow command не обращайте внимания, издержки тестого кода :))

Цитата

1.thumb.jpg.56a061a265ddd5fbac053cf3bccf631b.jpg

 

Изменено пользователем Salk

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Круто, спасибо, вещь реально прикольная, а не подскажите еще как с помощью нее знаковые числа выводить? :)

Если я правильно понял, то работать надо с ней так:

int i;
char str[80];

// преобразование 
sscanf((char *)UserTxBufferFS, "%s %d", str, &i);
sprintf((char *)UserTxBufferFS, "%s %d\n\r", str, i);
// вывод в порт
CDC_Transmit_FS((uint8_t*)UserTxBufferFS, strlen((const char*)UserTxBufferFS));

Вывод: "hp 22"; 22 - в данном случае беззнаковое число, что очень круто и минималистично! :)

При попытке подставить определить "%f": 

float i;
char str[80];

sscanf((char *)UserTxBufferFS, "%s %f", str, &i);
sprintf((char *)UserTxBufferFS, "%s %.3f\n\r", str, i);
CDC_Transmit_FS((uint8_t*)UserTxBufferFS, strlen((const char*)UserTxBufferFS));

Выдает "0.000".  Включение параметра "-u_scanf_float", ситуацию не исправляет, только память сжирает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тут лимит в одно слово на ответ что-ли? Я вроде в разделе "для новичков" и гуру из себя не строю. Можно как-то поразвернутее, что-ли...

Что "МОЗГ"? Функция такая есть, в <brain.h>? Скиньте, скачаю...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Работайте со строкой как с символами и забейте на все str...

Копируйте в один до первого символа 0-9 и потом копируйте во второй.

Только не определено, что делать со строкой Read123Write

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

16 hours ago, Salk said:

Недавно узнал и начал активно использовать библиотеку string.h.

Код из первого поста - жесть. Откройте для себя, что в string.h есть ещё много других функций, например strpbrk и strcpy

И sscanf конечно хорошо, но он очень жирный (много кода за собой тянет). Если у вас нет проблем с размером кода, советую смотреть в сторону регулярных выражений.

15 hours ago, Salk said:

Круто, спасибо, вещь реально прикольная, а не подскажите еще как с помощью нее знаковые числа выводить? :)

У вас и есть знаковое (формат %d). Беззнаковое будет %u. А %f - это вообще плавающая точка

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

44 минуты назад, xvr сказал:

Код из первого поста - жесть.

А что конкретно в том коде "жесть"? Мне для общего понимания и развития - не правильно воспользовался функцией strstr или поиск строк через цикл выглядит крипово? :)

44 минуты назад, xvr сказал:

А %f - это вообще плавающая точка

Да, простите, имел ввиду числа с плавающей точкой. Через sscanf так и не смог их вывести, память сжирает с аппетитом конечно, а результата нет. Пробовал через точку и запятую числа вводить, через пробел - никак. Среда STM32CubeIde.

Нашел и воспользовался функцией strtod, предварительно очистив массив от команд, код получился таким:

// парсинг чисел в массиве
char *tstr;				// Указатель, используемый для определения завершения преобразования строки
double d = 0;				// итог парсинга числа
uint8_t Array_of_Numbers[len];

/* где-то тут
// Отделяем команды из массива принятых данных, зная заранее их длину (оставляю все, что после)
*/

// Указатель, на непреобразованный остаток строки Array_of_Numbers
char *nstr = (char *)Array_of_Numbers;
while(1)
{
  tstr = nstr;
  // Преобразуем очередной участок строки
  d = strtod (nstr, &nstr);
  if (d == 0 && tstr == nstr) break; else f_chislo_V = d;
  HAL_IWDG_Init(&hiwdg);	// добавить таймаут
}

// выводим полученные данные в порт
sprintf((char *)UserTxBufferFS, "%.3f\n", f_chislo_V);
CDC_Transmit_FS((uint8_t*)UserTxBufferFS, strlen((const char*)UserTxBufferFS));
HAL_Delay(1);

Ввод: "Read13.3" (Read - команда распознана, ранее в коде удаляется из массива, оставляя только числовую часть)
Вывод: (парсим число) "13.300"
  
Ввод: "13.3"
Вывод: "13.300"
  
Ввод: "ffgg13.3"
Вывод: "Unknow command" (число не парсится, т.к. присутствуют недопустимые для чисел символы; "ffgg" не удаляется, т.к. это не является распознанной командой)
  
Ввод: "Read 13Read"
Вывод: "13.000" (последний Read так же не учитыввается)

Не сильная "жесть"? Функционально код меня полностью устраивает. strpbrk - тоже вещь, поможет в отделении чисел из общего массива.

44 минуты назад, xvr сказал:

регулярных выражений

Вы имели ввиду: regex.h, regex_fns.h из glibc. В чем им главное преимущество?

Спасибо за ответы.

Изменено пользователем Salk

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я в таких метстах старюсь обзодиться без копирования туда-сюда... птолько пермещая указателти/индексы. 
Кроме строковых есть и просто функции сравнения - memcmp, memchr.

Spoiler

static const uint8_t * findpattern(const uint8_t * buff, unsigned size, const void * pbuff, unsigned psize)
{
	while (size >= psize)
	{
		if (memcmp(buff, pbuff, psize) == 0)
			return buff + psize;		// found
		++ buff;
		-- size;
		const void * p = memchr(buff, * (const uint8_t *) pbuff, size);
		if (p != NULL)
		{
			ptrdiff_t skip = (uintptr_t) p - (uintptr_t) buff;
			size -= skip;
			buff += skip;
		}
		else
		{
			break;
		}
	}
	return NULL;
}

static const uint8_t * findpatternflex(const uint8_t * buff, unsigned size, const char * pbuff, unsigned psize)
{
	// поиск пробела - разделителя ищущихся литерально последоватиельностей
	const char * patspace = (const char *) memchr(pbuff, ' ', psize);
	if (patspace != 0)
	{
		// пробел есть. используем часть до него как литерал.
		unsigned pat1len = patspace - pbuff;
        const char * const pat1 = pbuff;
		unsigned pat2len = psize - pat1len - 1;
        const char * const pat2 = pbuff + pat1len + 1;

        for (;;)
        {
            unsigned skipped = 0;
 		    const uint8_t * f = findpattern(buff, size, pat1, pat1len);
		    if (f == NULL)
			    return NULL;

		    // Первая часть шаблона совпала. Пропускаем возможно имеющиеся разделители
		    const ptrdiff_t skip = f - buff;
		    buff += skip;
		    size -= skip;

		    while (size != 0 && isspace(* buff))
		    {
			    -- size;
			    ++ buff;
                ++ skipped;
		    }
		    if (size == 0)
			    return NULL;	// для второй части нет уже в чем искать

            if (pat2len == 0 && skipped != 0)
            {
                // второй части шаблона нет, но были пробелы - считаем шаблон совпавшим
                return buff;
            }

 		    const uint8_t * f2 = findpattern(buff, size, pat2, pat2len);
		    if (f != NULL)
			    return f2;
            // вторая часть не совпала - продолждаем поиск первой
        }
        // не нашёлся составной шаблон
        return NULL;
	}
	// обычный поиск первой литерально совпадающей последовательности
	return findpattern(buff, size, pbuff, psize);
}



static const uint8_t * findpatternsmart(const uint8_t * buff, unsigned size, const char * s)
{
	return findpatternflex(buff, size, s, strlen(s));
}

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 3/13/2021 at 12:25 PM, Salk said:

Не сильная "жесть"?

Уже гораздо лучше

On 3/13/2021 at 12:25 PM, Salk said:

Вы имели ввиду: regex.h, regex_fns.h из glibc. В чем им главное преимущество?

Можно и их. Преимущество в том, что всю вашу команду вместе с любыми числами внутри можно записать одной регуляркой, а потом проверить одним вызовом. Сразу получите всё - и команды отдельно и числа отдельно. И проверку на синтаксис

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

относительно исходного кода

"а вот уже:"ddff43" - считает первое вхождение только с числа "3" и обрезает "4"."

А чего тут удивляться? Идет цикл по цифрам 1,2,3...0 и сравнение c массивом. Ясно что он 3 находит первым (чем 4) и .. break. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...