2017-12-02 10 views
1

더 구체적으로 말하자면, .lines().filter_map()으로 특정 행을 걸러 내고있는 표준을 취하고 있는데 그 결과를 csv::Reader과 같이 사용하고 싶습니다.Iterator <Item=String>에 대해 std :: io :: Read를 구현하는 방법은 무엇입니까?

당신이 정말로 메모리에 모두로드 피하기 위해 필요한 경우
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"); 

, 그것은을 구현하는 것이 가능하다 :

+0

이 질문은 irc.mozilla.org의 #rust에있는 Rust IRC 사용자 "njoodle"에서 왔지만 그 시간 동안 IRC 로깅이 작동하지 않는 것으로 보입니다. 그래서 나는 완전한 authorship 링크를 제공 할 수 없습니다. – Shepmaster

답변

1

가장 간단한 해결책은 거대한 버퍼로 한 번에 모든 입력을 읽고 그에서 읽을 것 래퍼 (wrapper)하지만 사용 가능한 바이트 수와 읽을 바이트 수가 항상 일치하지 않기 때문에 약간의 실수가 있습니다.

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의 간단한 구현이 당신이 부분 읽기 반환 할 수있는 경우 : 당신은 더 많은 데이터를 읽는 계속받을 때때로 당신의 위치를 ​​추적하고 일부 임시 값에 유지해야합니다. Sheemaster와 비슷한 구조로 시작하십시오.

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