Tengo un archivo de texto de 100 GB, que es un volcado de BCP desde una base de datos. Cuando intento importar con BULK INSERT
, me sale un error críptico en la línea número 219506324. Antes de resolver este problema me gustaría ver esta línea, pero por desgracia mi método favorito deManera eficiente de leer un número de línea específico de un archivo. (BONUS: Error de impresión manual de Python)
import linecache
print linecache.getline(filename, linenumber)
es lanzar una MemoryError
. Curiosamente the manual says que "Esta función nunca arrojará una excepción". En esta archivo grande que arroja una, ya que tratar de leer la línea número 1, y tengo aproximadamente 6 GB RAM libre ...
Me gustaría saber cuál es el más elegante método para llegar a esa línea inalcanzable . Las herramientas disponibles son Python 2, Python 3 y C# 4 (Visual Studio 2010). Sí, entiendo que siempre puedo hacer algo como
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());
}
que funcionaría, pero dudo que sea la manera elegante más.
EDIT: Estoy esperando para cerrar este hilo, porque el disco duro que contiene el archivo está siendo utilizado en este momento por otro proceso. Voy a probar los métodos sugeridos y los tiempos del informe. Gracias a todos por sus sugerencias y comentarios.
Los resultados están en Implementé los métodos de Gabes y Alexes para ver cuál era más rápido. Si estoy haciendo algo mal, dígalo. Voy por la línea número 10 millones en mi archivo de 100GB usando el método que Gabe sugirió y luego uso el método que Alex sugirió y que traducido libremente a C# ... Lo único que estoy agregando de mí mismo es la primera lectura en un 300 MB de archivo en la memoria solo para borrar la caché HDD.
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)
La aplicación del método de Gabe es el siguiente:
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);
mientras que el método de Alex es un poco Tricker:
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);
Así que a menos que Alex le importa a comentar que marcaré solución de Gabe como aceptado.
¿Le sugiero que ejecute su archivo gigante a través de una utilidad como * nix 'split' para descomponerlo en un conjunto de archivos más pequeños con los que sería más fácil trabajar? http://ss64.com/bash/split.html – Amber
Cuando se trata de archivos de 100 GB, agregar un pase adicional para dividir los datos, crear otros 100 GB de datos, es un gran golpe de rendimiento, que debe ser evitable. –
Elegante, shmelegant. No olvide que el problema original fue el problema de inserción de la base de datos, no la forma más hábil de leer la línea 'n' del archivo de datos grandes z.dat. Escribe el forro C# cuatro, déjalo correr durante 10 minutos, consigue la línea problemática y continúa. O use 'split' para dividir el archivo como se sugirió @Amber, y vea si simplemente usar archivos más pequeños es suficiente. (Mi sospecha es que BULK INSERT está tratando de hacer todas estas inserciones en una sola transacción, y está explotando algún archivo de base de datos antes de la imagen, ya que tiene que ser ROLLBACK. Vea si tiene una opción BULK INSERT COMMIT EVERY 1000000.) – PaulMcG