Элегантный алгоритм разбора потока данных на запись - PullRequest
1 голос
/ 17 марта 2010

Я взаимодействую с аппаратным устройством, которое передает данные в мое приложение через Wi-Fi. Данные передаются просто отлично. Данные содержат символьный заголовок (DATA :), который указывает на начало новой записи. Проблема заключается в том, что данные, которые я получаю, не обязательно попадают на границу заголовка, поэтому я должен собирать данные, пока то, что я захватил, не содержит заголовок. Затем все, что предшествует заголовку, переходит в предыдущую запись, а все, что идет после него, входит в новую запись. У меня это работает, но мне интересно, если кто-то делал это раньше и у него есть хороший компьютерный способ решения проблемы.

Вот что я делаю:

  1. Преобразовать NSData текущего чтения в NSString

  2. Добавить строку NSSt в строку заполнителя

  3. Проверка строки заполнителя для заголовка (DATA :). Если заголовка нет, просто дождитесь следующего чтения.

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

  5. Возьмите все, что отображается после заголовка, и поместите его в заполнитель записи, чтобы к нему можно было добавить в следующем чтении. Повторите шаги 3 - 5.

Дайте мне знать, если вы видите какие-либо недостатки с этим или у вас есть предложение для лучшего пути.

Кажется, для этого должен быть какой-то шаблон проектирования, но я не могу придумать один.

Спасибо.

ОБНОВЛЕНИЕ: Вот немного кода:

uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {    
    [data appendBytes:(const void *)buf length:len];
    int bytesRead;
    bytesRead += len;
} else {
    NSLog(@"No data.");
}

Как изменить этот код, чтобы реализовать конечный автомат?

Ответы [ 2 ]

1 голос
/ 17 марта 2010

Это классическая проблема конечного автомата.Многие протоколы данных, которые основаны на потоке, могут быть описаны с помощью конечного автомата.

В основном у вас есть состояние и переход.У Boost есть библиотека конечных автоматов, но она может быть излишней.Вы можете реализовать его как переключатель.

while(stream.hasData) {
char nextInput = stream.get();
switch(currentState) {
  case D: {
     if(nextInput == A)
       currentState = A;
     else
       currentState = D; //die 
  } case A: {
    //Same for A
  }
}
}

Запрошенная разработка:
В основном посмотрите на диаграмму ниже ... это конечный автомат.В любой момент времени машина находится в одном состоянии.Каждый раз, когда персонаж вводится в конечный автомат, происходит переход, и текущее состояние перемещается.(возможно, вернуться в то же состояние).Поэтому все, что вам нужно сделать, это смоделировать ваши сетевые данные как конечный автомат, а затем внедрить этот компьютер.Есть библиотеки, которые выкладывают это для вас, тогда все, что вам нужно сделать, это реализовать именно то, что происходит при каждом переходе.Для вас это, вероятно, означает интерпретацию или сохранение байта данных.Интерпретация зависит от того, какой переход.Переход зависит от текущего состояния и текущего входа.Вот пример FSM.

альтернативный текст http://www.freeimagehosting.net/uploads/b1706f2a8d.png
Обратите внимание, что при вводе символов DATA: состояние перемещается на последний круг.Любая другая последовательность будет сохранять состояние в одном из первых 5 состояний.(верхний ряд) Вы также можете разделить.Таким образом, FSM может принимать решения, поэтому, если вы получаете последовательность, подобную DATA2:, то вы можете разветвляться с этой машины в data2: part и по-разному интерпретировать ее в совершенно другой части машины.

1 голос
/ 17 марта 2010

Похоже, я бы это сделал. Единственное, что я могу сделать по-другому, это написать категорию NSData, которая выполняет для меня линейный поиск DATA:, просто чтобы сэкономить на преобразовании ее в строку. Это не так сложно сделать. Что-то вроде:

@interface NSData (Search)

- (NSRange) rangeOfData:(NSData *)aData;

@end

@implementation NSData (Search)

- (NSRange) rangeOfData:(NSData *)aData {
  const void * bytes = [self bytes];
  NSUInteger length = [self length];

  const void * searchBytes = [aData bytes];
  NSUInteger searchLength = [aData length];
  NSUInteger searchIndex = 0;

  NSRange foundRange = {NSNotFound, searchLength};
  for (NSUInteger index = 0; index < length; index++) {
    if (bytes[index] == searchBytes[searchIndex]) {
      //the current character matches
      if (foundRange.location == NSNotFound) {
        foundRange.location = index;
      }
      searchIndex++;
      if (searchIndex >= searchLength) { return foundRange; }
    } else {
      searchIndex = 0;
      foundRange.location = NSNotFound;
    }
  }
  return foundRange;
}

@end

Тогда вы можете просто использовать:

NSData * searchData = [@"DATA:" dataUsingEncoding:NSUTF8StringEncoding];
while(receivingData) {
  if ([receivedData rangeOfData:searchData].location != NSNotFound) {
    //WOOO!
  }
}

(предупреждение: набрано в браузере)

...