2017-12-02 7 views
1

具体的には、私は.lines().filter_map()で特定の行を除外して標準を取ります。その結果をcsv::Readerとしたいと思います。イテレータ用のstd :: io :: Readを実装するには<Item=String>?

+0

この質問は、irc.mozilla.orgの#rustのRust IRCユーザー "njoodle"に由来していますが、その間にIRCログが機能していないように見えるので、より完全なauthorshipリンクを提供することはできません。 – Shepmaster

答えて

1

最も簡単な解決策は巨大なバッファに一度にすべての入力を読み、そこから読み取るために、次のようになります。

let iterable = ["h", "e", "l", "l", "o"]; 
let combined_string: String = iterable.iter().cloned().collect(); 
let bytes = combined_string.into_bytes(); 

let mut buf = vec![]; 
let bytes = (&bytes[..]).read_to_end(&mut buf).unwrap(); 
assert_eq!(&buf[..bytes], b"hello"); 

あなたが本当にメモリにすべてのロードを避けるために必要であれば、それが実現することが可能ですラッパーを使用することができますが、使用可能なバイト数と読み取るバイト数が常に一致するとは限らないため、いくつかの厄介なビットがあります。あなたは自分の位置を追跡し、時にはそれ以上のデータから読み続けてもらうために、いくつかの一時的な値に保持する必要があります。

use std::io::{self, Read}; 
use std::cmp; 

/// Eagerly consumes elements from the underlying iterator instead of 
/// returning partial reads. 
struct IteratorAsRead<I> 
where 
    I: Iterator, 
{ 
    iter: I, 
    value: Option<I::Item>, 
    offset: usize, 
} 

impl<I> IteratorAsRead<I> 
where 
    I: Iterator, 
{ 
    pub fn new<T>(iter: T) -> Self 
    where 
     T: IntoIterator<IntoIter = I, Item = I::Item>, 
    { 
     IteratorAsRead { 
      iter: iter.into_iter(), 
      value: None, 
      offset: 0, 
     } 
    } 
} 

impl<I> Read for IteratorAsRead<I> 
where 
    I: Iterator, 
    I::Item: AsRef<[u8]>, 
{ 
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 
     let mut copied = 0; 
     loop { 
      match self.value.take() { 
       None => { 
        match self.iter.next() { 
         None => { 
          return Ok(copied); 
         } 
         Some(value) => { 
          self.value = Some(value); 
         } 
        } 
       } 
       Some(original_value) => { 
        let entire_value_len = { 
         let entire_value = original_value.as_ref(); 

         // Skip over bytes we already copied 
         let value = &entire_value[self.offset..]; 
         let buf = &mut buf[copied..]; 

         // Make the slices the same length 
         let len_to_copy = cmp::min(value.len(), buf.len()); 
         let value = &value[..len_to_copy]; 
         let buf = &mut buf[..len_to_copy]; 

         // Copy 
         buf.copy_from_slice(value); 

         // Advance our values 
         copied += len_to_copy; 
         self.offset += len_to_copy; 

         entire_value.len() 
        }; 

        // If we completely used the value, reset our counters, 
        // otherwise put it back for the next call. 
        if self.offset == entire_value_len { 
         self.offset = 0; 
        } else { 
         self.value = Some(original_value); 
        } 
       } 
      } 

      // If we've filled the buffer, return it 
      if copied == buf.len() { 
       return Ok(copied); 
      } 
     } 
    } 
} 

#[test] 
fn small_pieces_are_combined() { 
    let iterable = ["h", "e", "l", "l", "o"]; 
    let mut reader = IteratorAsRead::new(&iterable); 

    let mut buf = vec![]; 
    let bytes = reader.read_to_end(&mut buf).unwrap(); 
    assert_eq!(&buf[..bytes], b"hello"); 
} 

#[test] 
fn partial_reads() { 
    let iterable = ["hello"]; 
    let mut reader = IteratorAsRead::new(&iterable); 

    let mut buf = [0; 2]; 

    let bytes = reader.read(&mut buf).unwrap(); 
    assert_eq!(&buf[..bytes], b"he"); 

    let bytes = reader.read(&mut buf).unwrap(); 
    assert_eq!(&buf[..bytes], b"ll"); 

    let bytes = reader.read(&mut buf).unwrap(); 
    assert_eq!(&buf[..bytes], b"o"); 
} 
2

あなたはそれを読み込む部分戻ることができた場合Read::readの簡単な実装があります。 Shepmasterと同様の構造から始めましょう。

use std::io::{self, Read}; 

struct IteratorAsRead<I> 
where 
    I: Iterator, 
{ 
    iter: I, 
    leftover: Option<(I::Item, usize)>, 
} 

impl<I> IteratorAsRead<I> 
where 
    I: Iterator, 
{ 
    pub fn new<T>(iter: T) -> Self 
    where 
     T: IntoIterator<IntoIter = I, Item = I::Item>, 
    { 
     IteratorAsRead { 
      iter: iter.into_iter(), 
      leftover: None, 
     } 
    } 
} 

すると最初から読むために空でない文字列を見つけるバッファにそれを書き込もうとすると、必要であれば最終的には任意の残り物を格納することで機能を実装します。

impl<I> Read for IteratorAsRead<I> 
where 
    I: Iterator, 
    I::Item: AsRef<[u8]>, 
{ 
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 
     let (leftover, skip) = match self.leftover.take() { 
      Some(last) => last, 
      None => match self.iter.find(|next| !next.as_ref().is_empty()) { 
       Some(next) => (next, 0), 
       None => return Ok(0), 
      } 
     }; 

     let read = (&leftover.as_ref()[skip..]).read(buf)?; 

     if skip + read < leftover.as_ref().len() { 
      self.leftover = Some((leftover, skip + read)); 
     } else { 
      self.leftover = None; 
     } 

     return Ok(read); 
    } 
} 
+0

私は 'std :: io :: Cursor'を使ってもっと簡単なバージョンを投稿しました。私の答えを見てください。 –

2

標準ライブラリは、バッファ内の位置と共にバッファをラップタイプstd::io::Cursorを提供します。これはさらにVeedrac's answerで与えられるコードを簡略化するために使用することができる。

use std::io::{self, Cursor, Read}; 

struct IteratorAsRead<I> 
where 
    I: Iterator, 
{ 
    iter: I, 
    cursor: Option<Cursor<I::Item>>, 
} 

impl<I> IteratorAsRead<I> 
where 
    I: Iterator, 
{ 
    pub fn new<T>(iter: T) -> Self 
    where 
     T: IntoIterator<IntoIter = I, Item = I::Item>, 
    { 
     let mut iter = iter.into_iter(); 
     let cursor = iter.next().map(Cursor::new); 
     IteratorAsRead { iter, cursor } 
    } 
} 

impl<I> Read for IteratorAsRead<I> 
where 
    I: Iterator, 
    Cursor<I::Item>: Read, 
{ 
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 
     while self.cursor.is_some() { 
      let read = self.cursor.as_mut().unwrap().read(buf)?; 
      if read > 0 { 
       return Ok(read); 
      } 
      self.cursor = self.iter.next().map(Cursor::new); 
     } 
     Ok(0) 
    } 
} 

非語彙的寿命を使用して、read()メソッド内のコードはわずかに(play link)を簡略化することができます。

+0

インプラントカーソルのために読む T:AsRef <[u8]> 'がありますので、要素タイプを制限する必要はありません。 – Veedrac

+0

@Veedracあなたはそうです、私はその制限は必要ありません。私は主に 'Cursor 'を' Option'にラップするのを避けるために導入しましたが、それでもそれはもっと一般的にすることができます。 –

+0

デフォルトを使用する場合、コンストラクタは静的なサイズの配列の場合と同様に、空でない型で動作するために 'cursor.set_position(next.len())'を呼び出す必要があります。 – Veedrac

関連する問題