2016-05-18 13 views
4

Я пытаюсь эффективно разобрать CSV-файлы по строкам без лишнего выделения памяти.Как назначить кусочки при переходе через Vec в ржавчине без копирования?

Поскольку мы не можем индексировать в строки в Русте, моя идея состояла в том, чтобы создать-структуру для каждой строки, которая имеет в собственности Vec<char> из строки символов и несколько &[char] срезов, представляющих места в этом Vec полей, которые требуют дальнейшая обработка.

Я поддерживаю только английский язык, поэтому нет необходимости в графах Unicode.

Я хватаю каждую строку из BufReader, собрать его в мою Vec<char>, а затем перебирать символы заметить правильные смещения для каждого поля среза:

let mut r_line: String; 
let mut char_count: usize; 
let mut comma_count: usize; 
let mut payload_start: usize; 
for stored in &ms7_files { 
    let reader = BufReader::new(File::open(&stored.as_path()).unwrap()); 
    for line in reader.lines() { 
     r_line = line.unwrap().to_string(); 
     let r_chars: Vec<char> = r_line.chars().collect(); 
     char_count = 0; 
     comma_count = 0; 
     payload_start = 0; 
     for chara in r_chars { 
      char_count += 1; 
      if chara == ',' { 
       comma_count += 1; 
       if comma_count == 1 { 
        let r_itemid = &r_chars[0..char_count - 1]; 
        payload_start = char_count + 1; 
       } else if comma_count == 2 { 
        let r_date = &r_chars[payload_start..char_count - 1]; 
        let r_payload = & r_chars[payload_start..r_line.len() - 1]; 
       } 
      } 
     } 
     // Code omitted here to initialize a struct described in my 
     // text above and add it to a Vec for later processing 
    } 
} 

Все идет гладко до тех пор, код внутри if испытаний на comma_count, где я пытаюсь создать ломтики char в Vec. Когда я пытаюсь скомпилировать, я получаю ужас:

proc_sales.rs:87:23: 87:30 error: use of moved value: `r_chars` [E0382] 
proc_sales.rs:87      let r_itemid = &r_chars[0..char_count - 1]; 
                 ^~~~~~ 
proc_sales.rs:87:23: 87:30 help: run `rustc --explain E0382` to see a detailed explanation 
proc_sales.rs:82:17: 82:24 note: `r_chars` moved here because it has type `collections::vec::Vec<char>`, which is non-copyable 
proc_sales.rs:82   for chara in r_chars { 
            ^~~~~~~ 

для каждой попытки создания среза. Я могу понять, почему компилятор жалуется. То, что я пытаюсь понять, - это лучшая стратегия сбора и обработки этих данных, не прибегая к большому количеству копий и клонирования. Черт, если бы я мог оставить оригинал String (для каждой файловой строки), принадлежащий BufReader, и просто держись за кусочки в это, я бы!

Не стесняйтесь прокомментировать исправление вышеуказанного кода, а также предложения по альтернативным подходам к этой проблеме, которые ограничивают ненужное копирование.

+1

Я не могу проверить, потому что я нахожусь в поезде .. но я готов поспорить, потому что ваш цикл for - это, по сути, синтаксический сахар для 'r_chars.into_iter()' .., который будет владеть. Если вместо этого явно использовать 'для chara в r_chars.iter()' .., то он вернет ссылки. –

+0

** Определенно ** используйте [csv crate] (https://github.com/BurntSushi/rust-csv). «На самом низком уровне анализатор может декодировать CSV со скоростью около 200 МБ/с». И это влечет за собой нулевые распределения. – Shepmaster

+0

* Так как мы не можем индексировать строки в Rust, * - уверен, что вы можете. Вам просто нужно использовать байтовые смещения, которые лежат на границах символов UTF-8. Существуют даже итераторы, такие как ['char_indices'] (http://doc.rust-lang.org/std/primitive.str.html#method.char_indices). – Shepmaster

ответ

3

BufReader::lines возвращает итератор, который производит Result<String> элементов. Когда на такой элемент вызывается unwrap, он всегда выделяет новый String (обратите внимание, что в line.unwrap().to_string(), to_string() является избыточным).

Если вы хотите минимизировать выделение, вы можете использовать BufReader::read_line.

Чтобы разделить поля CSV-линии, вы можете использовать str::split.

Вот упрощенная версия кода:

use std::io::{BufRead, BufReader}; 
use std::fs::File; 

fn main() { 
    let mut line = String::new(); 
    let ms7_files = ["file1.cvs", "file2.cvs"]; 
    for stored in &ms7_files { 
     let mut reader = BufReader::new(File::open(stored).unwrap()); 
     while reader.read_line(&mut line).unwrap() > 0 { 
      // creates a scope to the iterator, so we can call line.clear() 
      { 
       // does not allocate 
       let mut it = line.split(','); 
       // item_id, date and payload are string slices, that is &str 
       let item_id = it.next().expect("no item_id fied"); 
       let date = it.next().expect("no date field"); 
       let payload = it.next().expect("no payload field"); 
       // process fields 
      } 
      // sets len of line to 0, but does not deallocate 
      line.clear() 
     } 
    } 
} 

Вы также можете взглянуть на various обрешетки для работы с CSV-файлами.

+0

Эй, мне действительно нравится это решение. Очистка уже выделенной строки идеально вписывается в мой вопрос. Я знал о str :: split, но сам сворачивал, чтобы экспериментировать с низкоуровневой обработкой. Для производства я уверен, что буду использовать split. – ezekiel68

1

На ваш вопрос, как ответил @Simon Уайтхед, право собственности на r_chars было передано для создания IntoIterator, поэтому вы не можете использовать это.

Изменение кода с помощью

for chara in &r_chars 
// equivalent to 
// for chara in r_chars.iter() 

и копирование *chara всякий раз, когда вы хотите (дешево), вероятно, это исправить.

Для ответа @ malbarbo я бы посоветовал использовать BufReader::lines, если ваш csv содержит текстовые столбцы (которые могут содержать разрыв строки).

Глядя на crates.io Я вместо этого советую использовать либо испытанное сражение csv, либо если вам нужно немного больше производительности, но готовы к гораздо менее проверенному quick-csv.

+0

Спасибо за код, который исправляет мою оригинальную ошибку дизайна. У меня есть контроль над компоновкой CSV, которая не включает разрывы строк на запись, поэтому я собираюсь продолжить решение, предоставленное @malbarbo – ezekiel68

Смежные вопросы