Есть ли лучший способ достичь этого результата, чем использовать sscanf? - PullRequest
0 голосов
/ 07 января 2019

Мне нужно сканировать различные входящие сообщения из последовательного потока, чтобы проверить, содержат ли они эту строку:

«Все: Получено: switchX yy (y)»

, где X = от 1 до 9, а yy (y) «включено» или «выключено». то есть «Все: Получено: switch4 on» или «Все: Получено: switch2 off» и т. д.

Я использую следующий код на ATMega328 для проверки и передачи соответствующих переменных в функцию передач ():

valid_data = sscanf(serial_buffer, "Everything: Received: switch%u %s", &switch_number, command);
if(valid_data == 2)
{
  if(strcmp(command, "on") == 0)
    {
       transmit(switch_number, 1);
    }
    if(strcmp(command, "off") == 0)
    {
       transmit(switch_number, 0);
    }
}

Проверка запускается, когда ISR входа serial_buffer обнаруживает «\ n». '\ 0' добавляется к последовательному потоку для завершения строки.

Это работает, и мне не нужно больше места / вычислительной мощности, но мне просто интересно, является ли это наилучшим способом достижения требуемого результата?

1 Ответ

0 голосов
/ 07 января 2019

Это работает, и мне не нужно больше места / вычислительной мощности, но мне просто интересно, является ли это наилучшим способом достижения требуемого результата?

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

С этой точки зрения решение, основанное на sscanf(), хорошо, особенно со сравнительно простой строкой формата, такой как у вас на самом деле. Шаблон для строк, которые вы хотите сопоставить, довольно понятен в строке формата, и следующая логика также ясна и проста. В качестве бонуса, он также должен создавать небольшой код, так как библиотечная функция выполняет большую часть работы, и разумно надеяться, что реализация приложила некоторые усилия для оптимизации этой функции для хорошей производительности, так что это, вероятно, выигрыш даже для тех, кто критерии, которыми вы не были слишком обеспокоены.

Однако возможны некоторые проблемы с корректностью:

  • sscanf() буквально не соответствует пробелу. Серия из одного или нескольких пробельных символов в строке формата соответствует любой последовательности из нуля или более пробельных символов во входных данных.
  • sscanf() пропускает начальные пробелы перед большинством полей и, прежде всего, перед полями %u.
  • можно сканировать номера переключателей вне указанного диапазона от 1 до 9.
  • буфер команд легко может быть переполнен.
  • sscanf() будет игнорировать что-либо во входной строке после последнего совпадения поля

Все эти проблемы могут быть решены, если это необходимо. Вот, например, альтернатива, которая обрабатывает все из них, кроме количества пробелов между словами (но в том числе избегая пробелов между «переключателем» и цифрой):

unsigned char switch_number;
int nchars = 0;

int valid_data = sscanf(serial_buffer, "Everything: Received: switch%c %n",
        &switch_number, &nchars);

if (valid_data >= 1 && switch_number - (unsigned) '1' < 9) {
    char *command = serial_buffer + nchars;

    if (strcmp(command, "on") == 0) {
        transmit(switch_number - '0', 1);
    } else if (strcmp(command, "off") == 0) {
        transmit(switch_number - '0', 0);
    }  // else not a match
} // else not a match

Основные отличия от вашего включают

  • номер переключателя читается с помощью директивы %c, которая читает один символ без пропуска начального пробела. Условие проверки switch_number - (unsigned) '1' < 9 гарантирует, что показание символа находится между «1» и «9». Он использует тот факт, что беззнаковая арифметика оборачивается вокруг.

  • вместо чтения команды в отдельный буфер, длина ведущей подстроки фиксируется с помощью директивы %n. Это позволяет проверять весь хвост на «включено» и «выключено», тем самым устраняя необходимость в дополнительном буфере и позволяя отклонять строки с конечными словами.

  • Если вы хотите проверить, что все пробелы тоже точно совпадают, то %n также может помочь с этим. Например,

    if (nchars == 30 && serial_buffer[11] == ' ' && serial_buffer[21] == ' '
            serial_buffer[29] == ' ') // it's OK
    
...