2010-07-30 2 views
2

Я пытаюсь выяснить, как разделить файл на количество строк в каждом файле. Это файлы csv, и я не могу сделать это по байтам. Мне нужно сделать это по линиям. 20k кажется хорошим числом для каждого файла. Каков наилучший способ чтения потока в данной позиции? Stream.BaseStream.Position? Итак, если бы я прочитал первые 20k строк, я бы начал позицию на 39999? Откуда я знаю, что я почти в конце файлов? Спасибо всемРазделить большой файл на более мелкие файлы по количеству строк в C#?

+1

Вы пробовали 20k звонки Readline? – strager

+2

Вам не нужно искать вообще. Вы должны прочитать его по очереди, перейдя на новый файл, когда вы нажмете 20k. – Fosco

+0

Да, после того, как я написал это и пошел, чтобы стричь волосы. Мне стало ясно, что я могу прочитать его, а затем сделать readline. Благодаря! – DDiVita

ответ

3
using (System.IO.StreamReader sr = new System.IO.StreamReader("path")) 
{ 
    int fileNumber = 0; 

    while (!sr.EndOfStream) 
    { 
     int count = 0; 

     using (System.IO.StreamWriter sw = new System.IO.StreamWriter("other path" + ++fileNumber)) 
     { 
      sw.AutoFlush = true; 

      while (!sr.EndOfStream && ++count < 20000) 
      { 
       sw.WriteLine(sr.ReadLine()); 
      } 
     } 
    } 
} 
+0

Это кажется самым прямым для меня, хотя ради памяти я бы сбросил буфер записи с каждой записью, возможно. если каждая строка равна 100 байтам, что составляет 1000 строк 100 кбайт, а 20000 2 Мб, а не тонну памяти, но ненужную печать. –

+0

@Jimmy - я добавил «AutoFlush = True», который автоматически сбрасывается после каждой записи. –

+0

AutoFlush - это плохая идея в StreamWriter, поскольку она будет скрываться после каждого отдельного символа (я смотрел на код). Если вы не укажете размер буфера при создании StreamWriter, по умолчанию он будет содержать всего 128 символов, но это все равно лучше, чем никакого буфера. – Tergiver

3

Я хотел бы сделать это следующим образом:

// helper method to break up into blocks lazily 

public static IEnumerable<ICollection<T>> SplitEnumerable<T> 
    (IEnumerable<T> Sequence, int NbrPerBlock) 
{ 
    List<T> Group = new List<T>(NbrPerBlock); 

    foreach (T value in Sequence) 
    { 
     Group.Add(value); 

     if (Group.Count == NbrPerBlock) 
     { 
      yield return Group; 
      Group = new List<T>(NbrPerBlock); 
     } 
    } 

    if (Group.Any()) yield return Group; // flush out any remaining 
} 

// now it's trivial; if you want to make smaller files, just foreach 
// over this and write out the lines in each block to a new file 

public static IEnumerable<ICollection<string>> SplitFile(string filePath) 
{ 
    return File.ReadLines(filePath).SplitEnumerable(20000); 
} 

Разве это не достаточно для вас? Вы говорите о переходе с позиции на позицию, но я не понимаю, почему это необходимо.

+1

Это тоже работает !!!! Гоша. Мне нравится это место! – DDiVita

4
int index=0; 
var groups = from line in File.ReadLines("myfile.csv") 
      group line by index++/20000 into g 
      select g.AsEnumerable(); 
int file=0; 
foreach (var group in groups) 
     File.WriteAllLines((file++).ToString(), group.ToArray()); 
+0

Вам нужно использовать 'File.ReadLines' вместо' ReadAllLines' - 'ReadAllLines' считывает все это в память сразу. Кроме того, использование функции «index» в функции группировки, подобной этой, вызывает меня. – mquander

+0

изменен на ReadLines, спасибо –

+0

+1 Это очень интересное использование linq – BlackICE

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