es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

rust-csv analiza un campo de cadena envuelto en comillas dobles que contiene saltos de línea y utiliza comillas dobles como carácter de escape.

Tengo un archivo CSV similar a este (el archivo original es propietario, no se puede compartir). El separador es Tab.
Contiene una columna de descripción, cuyo texto está entre comillas dobles, puede contener cadenas entre comillas, donde, espera, la secuencia de escape también es comilla doble.

Tengo el siguiente código para analizar el archivo:

let mut reader = csv::ReaderBuilder::new()
.from_reader(file)
.deserialize().unwrap();

Siempre obtengo el error de deserialización del CSV:

Error de deserialización del CSV: registro 43747 (línea: 43748, byte: 21082563): falta el campo 'id'

Intenté usar flexible(true) y double_quotes(true) sin suerte.

¿Es posible analizar este tipo de campo y, de ser así, cómo?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Sí, es posible analizar este tipo de campo utilizando la biblioteca serde en Rust. Aquí tienes un ejemplo de cómo hacerlo:

    use serde::Deserialize;
    use serde::Deserializer;
    use serde::de::{self, Visitor, Unexpected};
    
    #[derive(Debug, Deserialize)]
    struct Record {
        id: u32,
        description: String,
        other_field: u32,
    }
    
    fn parse_escape_sequence<'de, D>(d: D) -> Result<string, d::error="">
    where
        D: Deserializer<'de>,
    {
        struct EscapeSequenceVisitor;
    
        impl<'de> Visitor<'de> for EscapeSequenceVisitor {
            type Value = String;
    
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("una secuencia de escape válida")
            }
    
            fn visit_str<e>(self, value: &str) -> Result<string, e="">
            where
                E: de::Error,
            {
                let mut result = String::new();
    
                let mut chars = value.chars().peekable();
    
                while let Some(c) = chars.next() {
                    if c == '"' {
                        if let Some(&next_c) = chars.peek() {
                            if next_c == '"' {
                                chars.next();
                                result.push('"');
                            } else {
                                return Err(de::Error::invalid_value(
                                    Unexpected::Char(c),
                                    &self,
                                ));
                            }
                        }
                    } else {
                        result.push(c);
                    }
                }
    
                Ok(result)
            }
        }
    
        d.deserialize_str(EscapeSequenceVisitor)
    }
    
    fn parse_csv(csv_string: &str) -> Result<><record>, csv::Error> {
        let mut reader = csv::ReaderBuilder::new()
            .delimiter(b'\t')
            .has_headers(false)
            .from_reader(csv_string.as_bytes());
        reader.deserialize()
            .filter_map(|result| {
                match result {
                    Ok((id, description, other_field)) => Some(Ok(Record {
                        id,
                        description: description.trim_matches('"')
                            .replace(r#""""#, "\"")
                            .replace(r"\r\n", "\n"),
                        other_field,
                    })),
                    Err(err) => Some(Err(err)),
                }
            })
            .collect()
    }
    
    let csv_string = r#"
    id  description other_field
    12 "Some Description"  34
    56 "Some
    Multiline
    ""With Escaped Stuff""
    Description"    78
    "#;
    
    let records = parse_csv(csv_string)?;
    
    println!("{:#?}", records);
    

    Las partes clave del código son la función parse_escape_sequence, que deserializa la secuencia de escape dentro de la cadena entre comillas; y la función parse_csv, que analiza la cadena CSV en un vector de estructuras Record. El campo description de la estructura Record puede contener cadenas de varias líneas y cadenas entre comillas con secuencias de escape. El código también admite un análisis flexible y comillas dobles.</string,></string,>

Comments are closed.