Как разобрать фрагмент ввода u16 с nom? - PullRequest
1 голос
/ 14 апреля 2019

Учитывая необработанный входной поток &[u16], как я могу использовать nom для его анализа, принимая во внимание, что nom ожидает &str в качестве ввода?

Например, учитывая следующие данные:

pub const RAW_INPUT: &[u16] = &[102, 111, 111];

Я хочу разобрать его в строку "foo".

Ответы [ 2 ]

0 голосов
/ 19 апреля 2019

Дано:

pub const RAW_INPUT: &[u16] = &[102, 111, 111];

Я закончил преобразование ввода в u8 сначала:

let xs = RAW_INPUT
    .iter()
    .flat_map(|x| x.to_be_bytes().to_vec())
    .collect::<Vec<u8>>();

А затем обычно анализирует его с помощью ном.

0 голосов
/ 15 апреля 2019

Есть несколько способов сделать это. Я ничего не знаю о Modbus, поэтому я предполагаю, что ввод выглядит как ваш RAW_INPUT выше. Во-первых, вы можете использовать as для приведения u16 к u8. Это приведет к тихому усечению значений больше 255. Другой, более безопасный подход - использовать std :: convert :: TryFrom :

Простые и безопасные преобразования типов, которые могут не получаться контролируемым образом при некоторых обстоятельствах. Это обратная TryInto.

Это полезно, когда вы выполняете преобразование типов, которое может быть тривиально успешным, но также может потребоваться специальная обработка. Например, невозможно преобразовать i64 в i32 с использованием признака From, поскольку i64 может содержать значение, которое i32 не может представлять, и поэтому преобразование может потерять данные. Это может быть обработано путем усечения i64 до i32 (по сути, давая значение i64 по модулю i32::MAX) или просто возвращая i32::MAX, или каким-либо другим методом. Черта From предназначена для идеальных преобразований, поэтому черта TryFrom сообщает программисту, когда преобразование типа может пойти плохо, и позволяет им решить, как с ним справиться.

Некоторый иллюстративный код, с которым вы можете поиграть на Rust Playground :

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;
    use std::num::TryFromIntError;
    use std::str;

    pub const RAW_BAD_INPUT: &[u16] = &[102, 111, 111, 300];
    pub const RAW_GOOD_INPUT: &[u16] = &[102, 111, 111];

    /// Converts using `as`. Demonstrates truncation.
    #[test]
    fn test_truncating() {
        let expected = vec![102, 111, 111, 44];  // Note: 44
        let actual = RAW_BAD_INPUT
            .iter()
            .map(|val| *val as u8)
            .collect::<Vec<u8>>();
        assert_eq!(expected, actual);
    }

    /// Demonstrates conversion using `TryFrom` on input with values that
    /// would be truncated
    #[test]
    fn test_try_from_bad() {
        let actual: Vec<Result<u8, TryFromIntError>> =
            RAW_BAD_INPUT.iter().map(|val| u8::try_from(*val)).collect();

        assert_eq!(actual[0].unwrap(), 102u8);
        assert_eq!(actual[1].unwrap(), 111u8);
        assert_eq!(actual[2].unwrap(), 111u8);
        assert!(actual[3].is_err());
    }

    /// Demonstrates conversion using `TryFrom` on input with values
    /// that would not be truncated. Also parses the Vec<u8> as a UTF-8
    /// encoded string
    #[test]
    fn test_try_from_ok() {
        let intermediate: Vec<u8> = RAW_GOOD_INPUT
            .iter()
            .map(|val| u8::try_from(*val).unwrap())
            .collect();
        let actual = match str::from_utf8(&intermediate) {
            Ok(s) => s,
            Err(e) => panic!("Invalid UTF-8: {}", e),
        };
        assert_eq!("foo", actual);
    }
}

Используя код в test_try_from_ok, теперь у вас должна быть String, содержащая данные, которые вы хотите проанализировать с nom.

...