Редактировать : информация о сравнительном тестировании ниже.
Я бы не использовал командлеты Powershell csv.Я бы использовал System.IO.StreamReader
или Microsoft.VisualBasic.FileIO.TextFieldParser
для чтения в файле построчно, чтобы избежать загрузки всего объекта в память, и я бы использовал System.IO.StreamWriter
, чтобы выписать его обратно.TextFieldParser
внутренне использует StreamReader
, но обрабатывает поля с разделителями, поэтому вам не нужно это делать, что очень полезно, если формат CSV не прост (например, содержит символы-разделители в полях в кавычках).
Я бы вообще не делал этого в Powershell, а скорее в приложении .NET, так как он будет намного быстрее, чем скрипт Powershell, даже если они используют одни и те же объекты.
Вот простой C # для простоговерсия, при условии отсутствия полей в кавычках и кодировки ASCII:
static void Main(){
string source = @"D:\test.csv";
string dest = @"D:\test2.csv";
using ( var reader = new Microsoft.VisualBasic.FileIO.TextFieldParser( source, Encoding.ASCII ) ) {
using ( var writer = new System.IO.StreamWriter( dest, false, Encoding.ASCII ) ) {
reader.SetDelimiters( ";" );
while ( !reader.EndOfData ) {
var fields = reader.ReadFields();
swap(fields, 1, 2);
writer.WriteLine( string.Join( ";", fields ) );
}
}
}
}
static void swap( string[] arr, int a, int b ) {
string t = arr[ a ];
arr[ a ] = arr[ b ];
arr[ b ] = t;
}
Вот версия Powershell:
[void][reflection.assembly]::loadwithpartialname("Microsoft.VisualBasic")
$source = 'D:\test.csv'
$dest = 'D:\test2.csv'
$reader = new-object Microsoft.VisualBasic.FileIO.TextFieldParser $source
$writer = new-object System.IO.StreamWriter $dest
function swap($f,$a,$b){ $t = $f[$a]; $f[$a] = $f[$b]; $f[$b] = $t}
$reader.SetDelimiters(';')
while ( !$reader.EndOfData ) {
$fields = $reader.ReadFields()
swap $fields 1 2
$writer.WriteLine([string]::join(';', $fields))
}
$reader.close()
$writer.close()
Я сравнил их оба с CSV-файлом с 3 столбцами и 10 000 000 строк.Версия C # заняла 171,132 секунды (чуть менее 3 минут).Версия Powershell заняла 2 364,995 секунды (39 минут 25 секунд).
Редактировать : почему мой черт возьми так долго.
Функция свопинга - огромное узкое место в моемВерсия Powershell.Замена его выводом в стиле '{0};{1};{2}'
, как в ответе Романа Кузьмина, сократило его до менее чем 9 минут.Замена TextFieldParser
более чем вдвое оставшимися до менее чем 4 минут.
Однако, версия консольного приложения .NET ответа Романа Кузьмина заняла 20 секунд.