官术网_书友最值得收藏!

Reading formatted data from files

As you will certainly have guessed at this point, we'll use iterators once again. This is what the loading function will look like:

fn line_to_slice(line: &str) -> Vec<u32> {
    line.split(" ").filter_map(|nb| nb.parse::<u32>().ok()).collect()
}

fn load_highscores_and_lines() -> Option<(Vec<u32>, Vec<u32>)> {
    if let Ok(content) = read_from_file("scores.txt") {
        let mut lines = content.splitn(2, "\n").map(|line| 
line_to_slice(line)).collect::<Vec<_>>(); if lines.len() == 2 { let (number_lines, highscores) = (lines.pop().unwrap(),
lines.pop().unwrap()); Some((highscores, number_lines)) } else { None } } else { None }
}

Once again, not easy to understand, at first sight. So let's explain all this!

fn line_to_slice(line: &str) -> Vec<u32> {

Our line_to_slice() function does the opposite of slice_to_string(); it transforms a &str into a slice of u32 (or &[u32]). Let's see the iterator now:

    line.split(" ").filter_map(|nb| nb.parse::<u32>().ok()).collect()

Just like last time, let's split the calls:

    line.split(" ")
      .filter_map(|nb| nb.parse::<u32>().ok())
      .collect()

A bit better! Now let's explain:

     line.split(" ")

We create an iterator that will contain all strings between spaces. So a b will contain a and b:

    .filter_map(|nb| nb.parse::<u32>().ok())

This method is particularly interesting since it's the merge of two others: filter() and map(). We already know map() but what about filter()? If the condition isn't verified (so if the returned value of the closure is false), the iterator won't pass the value to the next method call. filter_map() works the same at this point: if the closure returns None, the value won't be passed to the next method call.

Now let's focus on this part:

    nb.parse::<u32>().ok()

Here, we try to convert &str into u32. The parse() method returns a Result but the filter_map() expects an Option so we need to convert it. That's what the ok() method is for! If your Result is an Ok(value), then it'll convert it into a Some(value). However, if it's an Err(err), it'll convert it into a None (but you'll lose the error value).

To sum this up, this whole line tries to convert a &str into a number and ignores it if the conversion fails so it's not added to our final Vec. Amazing how much we can do with such small code!

And finally:

    .collect()

We collect all the successful conversions into a Vec and return it.

That's it for this function, now let's look at the other one:

    fn load_highscores_and_lines() -> Option<(Vec<u32>, Vec<u32>)> {

Here, if everything went fine (if the file exists and has two lines), we return an Option containing in the first position the highest scores and in the second position the highest number of lines:

    if let Ok(content) = read_from_file("scores.txt") {

So if the file exists and we can get its content, we parse the data:

    let mut lines = content.splitn(2, "\n").map(|line| 
line_to_slice(line)).collect::<Vec<_>>();

Another iterator! As usual, let's rewrite it a bit:

    let mut lines = content.splitn(2, "\n")
         .map(|line| line_to_slice(line))
         .collect::<Vec<_>>();

I think you're starting to get how they work, but just in case you don't know, here's how:

    content.splitn(2, "\n")

We make an iterator containing at most two entries (because of the 2 as the first argument) splitting lines:

    .map(|line| line_to_slice(line))

We transform each line into a Vec<u32> by using the function described in the preceding code:

    .collect::<Vec<_>>();

And finally, we collect those Vecs into a Vec<Vec<u32>>, which should only contain two entries.

Let's look at the next line now:

    if lines.len() == 2 {

As said before, if we don't have two entries inside our Vec, it means something is wrong with the file:

    let (number_lines, highscores) = (lines.pop().unwrap(), 
lines.pop().unwrap());

In case our Vec has two entries, we can get the corresponding values. Since the pop method removes the last entry of the Vec, we get them in reverse (even though we return high scores first then the highest number of lines):

    Some((highscores, number_lines))

Then everything else is just the error handling. As we said previously, if any error occurs, we return None. In this case, it's not really important to handle the error since it's just high scores. If we have errors with the sdl libraries, nothing will work as expected, so we need to handle them to avoid a panic.

It's now time to really start the game!

主站蜘蛛池模板: 新巴尔虎右旗| 宁陵县| 玉山县| 安溪县| 阿合奇县| 垦利县| 务川| 玉山县| 寿光市| 衡阳县| 绿春县| 河北省| 舒城县| 杨浦区| 舒城县| 南昌市| 监利县| 盐亭县| 会泽县| 囊谦县| 彰化市| 安宁市| 临夏市| 天水市| 泸定县| 革吉县| 邓州市| 三河市| 阿坝县| 古蔺县| 泽普县| 左云县| 巴中市| 饶平县| 横山县| 永平县| 阿勒泰市| 白城市| 乐昌市| 富平县| 阳朔县|