Help me. reading file and extract content

Here a minimal example:

fn find<'a>(input: &'a str, key: &str) -> Option<&'a str> {
    let mut found = false;
    for line in input.lines() {
        // ... you know you return the line AFTER the line where you found `key`, right?
        if found {
            return Some(line);
        } else if line.contains(key) {
            found = true;
        }
    }
    None
}

fn main() {
    // variables to store result
    let mut original_title: &str = "";
    
    let reader = "\
Original Title
    Paradise Highway 
Year
    2022
Country
    United States";

    let info_txt = reader.split("\n");
    for line in info_txt {
        if line.contains("Original Title") {
            original_title = find(&reader, &line).unwrap();
        }
    } // end for line
    println!("Original title is -> {}", original_title); // print empty
}

Playground.

Stdout:

Original title is ->     Paradise Highway 
2 Likes

Great!, very educational.

ERROR OUTPUT:
thread 'main' panicked at 'called Option::unwrap() on a None value', src/main.rs:92:51
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

when i use:

let reader = fs::read_to_string(filename)
                    .expect("Except: file info.txt !!!");

Jofas, what's happening to me ?

convert type ?

Your error probably originates in this line:

original_title = find(&reader, &line).unwrap();

If you can't find your key (&line), None is returned by your function, because we didn't find what we are searching for (the "Original Title"). That probably means that the file /Book/info.txt does not contain a line with "Original Title" in it.

the type of variables is:

let info_txt=reader.split("\n"); ---> core::str::iter::Split<&str>
let reader: &str = "...."; ---> &str

and the file root is ok:
/home/pichibw/info.txt

I think the problem is the content of /home/pichibw/info.txt. Your find function seems (to me) unable to locate "Original Title" in any of the lines of your file.

when print content of variable:
let info_txt=reader.split("\n");
println!("type of info_txt: {}", type_of(&info_txt)); // function to obtain the type
println!("Content of info.txt: {:?}", info_txt); // print content

OUTPUT:
type of info_txt: core::str::iter::Split<&str>
Content of info.txt: Split(SplitInternal { start: 0, end: 1361, matcher: StrSearcher { haystack: "\u{feff}\r\nOriginal Title\r\n Plane\r\nAKA\r\n The Plane\r\nYear\r\n ... etc etc

Thank you Windows for \r\n. Inside find we use .lines() to iterate over lines. In your main we use .split("\n"). What happens is that we search for "Original Title\r" as key, because splitting at \n won't remove the trailing \r from each line. .lines() removes the trailing \r from each line, which is why we never find a match. Replace the .split("\n") with .lines() in your main and it'll work:

for line in reader.lines() {
    if line.contains("Original Title") {
        original_title = find(&reader, &line).unwrap();
    }
}
2 Likes

Thank you Windows for \r\n jajaja
Great, Jofas. :hugs:

how would it be using match ??

You mean like

match line {
    _ if line.contains("Original Title") => { ... },
    _ if line.contains("AKA") => { ... },
    _ => { ... }
}

?

1 Like

what is more efficient ? if or match ?

Almost surely no difference, but if there is one it will be dwarfed by the surrounding IO and allocation, so don't worry about it.

3 Likes

If you want to work on the efficiency of your program, you should make your algorithm more efficient. It does a lot of needless iterations you can avoid.

Given the structure of your input (field name in one line followed by its value in the next line), we can extract all the fields in one single pass over your input, reducing the time complexity from O(lines * log(lines)) to O(lines):

fn main() {
    let mut original_title: &str = "";
    let mut year: &str = "";
    let mut country: &str = "";
    
    
    let reader = "\
Original Title
    Paradise Highway 
Year
    2022
Country
    United States";
    
    let mut lines = reader.lines();
    
    while let (Some(key), Some(value)) = (lines.next(), lines.next()) {
        if key.contains("Original Title") {
            original_title = value;
        } else if key.contains("Year") {
            year = value;
        } else if key.contains("Country") {
            country = value;
        }
    }
    
    println!("Original title is -> {}", original_title);
    println!("Year is -> {}", year);
    println!("Country is -> {}", country);
}

Playground.

2 Likes

Output empty:
Original title is:
Year is:
Country is:
Sinopsis is:

No it isn't. Did you check the linked Playground?

1 Like

Here an example that uses something closer to your input. Your files start with a line containing the special unicode character \u{feff}. We have to skip this line or else our algorithm is off, having keys in the position of values and values in the position of keys when we try and parse the input as lines. So all we got to do is skip reading the first line:

fn main() {
    let mut original_title: &str = "";
    let mut year: &str = "";
    let mut country: &str = "";
    
    
    let reader = "\u{feff}\r\nOriginal Title\r\n Plane\r\nAKA\r\n The Plane\r\nYear\r\n 2022\r\nCountry\r\n United States\r\n";
    
    let mut lines = reader.lines();
    
    // skip first line "\u{feff}"
    lines.next();
    
    while let (Some(key), Some(value)) = (lines.next(), lines.next()) {
        if key.contains("Original Title") {
            original_title = value;
        } else if key.contains("Year") {
            year = value;
        } else if key.contains("Country") {
            country = value;
        }
    }
    
    println!("Original title is -> {}", original_title);
    println!("Year is -> {}", year);
    println!("Country is -> {}", country);
}

Playground.


As side note, AFAICT, \u{feff} is the byte order mark for UTF-16, not UTF-8 which is the encoding Rust strings use. If your files are UTF-16 encoded and contain non-ASCII charakters, parsing your files as UTF-8 can fail in funny ways. I'm not too well read on unicode encodings, I just remember having to debug why my strings in Dart were looking funny when they came from my json API (Dart strings are UTF-16 and json is UTF-8 encoded).

1 Like

Work fine!. Jofas
a good practical example of the use WHILE LET.
Thanks a lot.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.