Эффективный способ чтения определенного номера строки файла. (БОНУС: Ошибка в руководстве по Python)

У меня есть текстовый файл размером 100 ГБ, который представляет собой дамп BCP из базы данных. Когда я пытаюсь импортировать его с помощью BULK INSERT , я получаю загадочную ошибку в строке 219506324. Прежде чем решить эту проблему, я хотел бы увидеть эту строку, но, увы, мой любимый метод

import linecache
print linecache.getline(filename, linenumber)

- это бросить MemoryError . Интересно, что в руководстве говорится , что «Эта функция никогда не вызовет исключение». В этом большом файле он выдает единицу, когда я пытаюсь прочитать строку № 1, и у меня есть около 6 ГБ свободной оперативной памяти. ...

Я хотел бы знать, какой метод наиболее изящен , чтобы добраться до этой недоступной линии. Доступными инструментами являются Python 2, Python 3 и C # 4 (Visual Studio 2010). Да, я понимаю, что всегда могу сделать что-то вроде

var line = 0;
using (var stream = new StreamReader(File.OpenRead(@"s:\source\transactions.dat")))
{
     while (++line < 219506324) stream.ReadLine(); //waste some cycles
     Console.WriteLine(stream.ReadLine());
}

, что бы сработало, но я сомневаюсь, что это самый элегантный способ.

РЕДАКТИРОВАТЬ: Я жду, чтобы закрыть этот поток, потому что жесткий диск, содержащий файл, сейчас используется другим процессом. Я собираюсь проверить оба предложенных метода и сообщить о времени. Спасибо всем за ваши предложения и комментарии.

Результаты приведены в . Я реализовал методы Габеса и Алекса, чтобы увидеть, какой из них был быстрее. Если я делаю что-то не так, скажи. Я использую 10-миллионную строку в моем 100-гигабайтном файле, используя метод, предложенный Гейбом, а затем метод, предложенный Алекс, который я свободно перевел на C # ... Единственное, что я добавляю от себя, - это сначала чтение в 300 Файл MB помещается в память только для очистки кэша жесткого диска.

const string file = @"x:\....dat"; // 100 GB file
const string otherFile = @"x:\....dat"; // 300 MB file
const int linenumber = 10000000;

ClearHDDCache(otherFile);
GabeMethod(file, linenumber);  //Gabe's method

ClearHDDCache(otherFile);
AlexMethod(file, linenumber);  //Alex's method

// Results
// Gabe's method: 8290 (ms)
// Alex's method: 13455 (ms)

Реализация метода Гейба выглядит следующим образом:

var gabe = new Stopwatch();
gabe.Start();
var data = File.ReadLines(file).ElementAt(linenumber - 1);
gabe.Stop();
Console.WriteLine("Gabe's method: {0} (ms)",  gabe.ElapsedMilliseconds);

Хотя метод Алекса немного сложнее:

var alex = new Stopwatch();
alex.Start();
const int buffersize = 100 * 1024; //bytes
var buffer = new byte[buffersize];
var counter = 0;
using (var filestream = File.OpenRead(file))
{
    while (true) // Cutting corners here...
    {
        filestream.Read(buffer, 0, buffersize);
        //At this point we could probably launch an async read into the next chunk...
        var linesread = buffer.Count(b => b == 10); //10 is ASCII linebreak.
        if (counter + linesread >= linenumber) break;
        counter += linesread;
    }
}
//The downside of this method is that we have to assume that the line fit into the buffer, or do something clever...er
var data = new ASCIIEncoding().GetString(buffer).Split('\n').ElementAt(linenumber - counter - 1);
alex.Stop();
Console.WriteLine("Alex's method: {0} (ms)", alex.ElapsedMilliseconds);

Так что, если Алекс не захочет комментировать, я отмечу решение Гейба как принятое.

6
задан alain.janinm 29 April 2012 в 20:18
поделиться