Sumit Kumar
Sumit Kumar

Reputation: 422

Configure newline characters while reading file

I am new to rust programming and trying to read a file line by line. It seems as it removes newline characters in the end and returns only the text. I am reading a metadata file where it has \n as well as \r\n. File sample is below where I have added \n and \r\n explicitly.

TAG 0\n
15:\n
32348SIPpTag091\n

SDP from 0 after offer
215:
v=0\r\n
o=user1 53655765 2353687637 IN IP4 172.31.8.95\r\n
s=-\r\n
c=IN IP4 172.31.8.95\r\n
t=0 0\r\n
m=audio 34218 RTP/AVP 8\r\n
c=IN IP4 172.31.8.95\r\n
a=direction:both\r\n
a=label:a_leg\r\n
a=rtpmap:8 PCMA/8000\r\n
a=sendonly\r\n
a=rtcp:34219\r\n

I am capturing this information in a map, where TAG 0 is my key and 32348SIPpTag091 is the value and this line 15: gives me the length of value. Based on this length I am figuring out whether I have received the value or not. But with my below code I am losing \r along with \n and because of that the length is not matching with the content. Is there a way in rust where I can configure to only look for \n for newline or not to remove those characters from the line. Here is my code

fn read_meta(path: &str) -> MetaInfo {
    let mut map = HashMap::new();
    let mut idx: String = String::from("");
    let mut data: String = String::from("");
    let mut dlen: i32 = -1;

    println!("reading from {}", path);
    let file = File::open(path).unwrap();
    let reader = BufReader::new(file);
    for line in reader.lines() {
        let line = line.unwrap();
        if idx.is_empty() {
            if line.len() <= 1 {
                continue;
            }
            idx = String::from(line.trim_end_matches("\n"));
        } else if dlen == -1 {
            let c = String::from(line.trim_end_matches("\n").trim_end_matches(":"));
            dlen = c.parse::<i32>().unwrap();
        } else {
            data = data + &line;
            if data.len() as i32 >= dlen {
                let diff = (data.len() as i32 - dlen) * -1;
                if diff < 0 {
                    let val = data.chars().take(dlen as usize).collect();
                    map.insert(idx.to_string(), val);
                } else {
                    map.insert(idx.to_string(), data);
                }
                idx = String::from("");
                dlen = -1;
                data = String::from("");
            }
        }
    }
    let meta_info = MetaInfo {
        index: String::from(map.get("PARENT").unwrap()),
        is_complete: map.contains_key("PARENT") && map.contains_key("SDP from 1 after answer")
    };
    return meta_info;

}

Upvotes: 3

Views: 1774

Answers (1)

Masklinn
Masklinn

Reputation: 42492

As you've noticed, the lines utility will strip out ending newlines (CR LF or LF alone) for convenience. This is not configurable

If you do not want this behaviour, you can use the lower-level read_line: it will read a line and put it in the buffer you provided including the EOL content, whether it's a lone LF or CR LF. It's not an iterable though so you'll have to process it by hand e.g.:

let mut buf = String::new();
while let Ok(n) = reader.read_line(&mut buf) {
    if n == 0 { break; } // eof

    // do stuff with `buf` string here

    buf.clear(); // otherwise the data will accumulate in your buffer
}

playground demo

Upvotes: 6

Related Questions