У меня есть множество файлов .CSV с уникальными именами, из которых мне нужно удалить первые 17 строк. Некоторые из этих файлов превышают 65534 строки, поэтому мой пакетный сценарий MORE/MOVE не работает. Ищем альтернативные решения.
@echo off
for %%a in (*.csv) do (
more +17 "%%a" >"%%a.new"
move /y "%%a.new" "%%a" >nul
)
Независимо от количества введенных строк, я хочу удалить 17 строк заголовков и создать новый файл со всеми оставшимися строками.
Команда FOR /F
имеет возможность пропуска.
Создайте свою собственную команду cut
. Это VBScript, перенесенный на VB.NET.
Резать
cut {t|b} {i|x} NumOfLines
Сокращает количество строк сверху или снизу файла.
t - top of the file
b - bottom of the file
i - include n lines
x - exclude n lines
Пример
cut t i 5 < "%systemroot%\win.ini"
Cut.bat
REM Cut.bat
REM This file compiles Cut.vb to Cut.exe
REM Cut.exe Removes specified from top or bottom of lines from StdIn and writes to StdOut
REM To use
REM cut {t|b} {i|x} NumOfLines
Rem Cuts the number of lines from the top or bottom of file.
Rem t - top of the file
Rem b - bottom of the file
Rem i - include n lines
Rem x - exclude n lines
Rem
Rem Example - Includes first 5 lines Win.ini
Rem
Rem cut t i 5 < "%systemroot%\win.ini"
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc.exe" /target:exe /out:"%~dp0\Cut.exe" "%~dp0\Cut.vb" /verbose
pause
Вырезать.vb
'DeDup.vb
Imports System
Imports System.IO
Imports System.Runtime.InteropServices
Imports Microsoft.Win32
Public Module DeDup
Sub Main
Dim Arg() As Object
Dim RS as Object
Dim LineCount as Object
Dim Line as Object
Arg = Split(Command(), " ")
rs = CreateObject("ADODB.Recordset")
With rs
.Fields.Append("LineNumber", 4)
.Fields.Append("Txt", 201, 5000)
.Open
LineCount = 0
Line=Console.readline
Do Until Line = Nothing
LineCount = LineCount + 1
.AddNew
.Fields("LineNumber").value = LineCount
.Fields("Txt").value = Console.readline
.UpDate
Line = Console.ReadLine
Loop
.Sort = "LineNumber ASC"
If LCase(Arg(0)) = "t" then
If LCase(Arg(1)) = "i" then
.filter = "LineNumber < " & LCase(Arg(2)) + 1
ElseIf LCase(Arg(1)) = "x" then
.filter = "LineNumber > " & LCase(Arg(2))
End If
ElseIf LCase(Arg(0)) = "b" then
If LCase(Arg(1)) = "i" then
.filter = "LineNumber > " & LineCount - LCase(Arg(2))
ElseIf LCase(Arg(1)) = "x" then
.filter = "LineNumber < " & LineCount - LCase(Arg(2)) + 1
End If
End If
Do While not .EOF
Console.writeline(.Fields("Txt").Value)
.MoveNext
Loop
End With
End Sub
End Module
Вот однострочное решение
for %%a in (*.txt) do powershell -Com "sc -Path '%%a' -Value (gc '%%a' | select -Skip 17)"
где gc
и sc
— псевдонимы по умолчанию для Get-Content
и Set-Content
соответственно. Смотрите также
Если ваши файлы огромны, лучше читать по строкам или блокам, что также можно легко реализовать с помощью файловых функций, [IO.File]::OpenText
или опции -ReadCount
Get-Content
в PowerShell.
Как упомянул Сквошман, for /f
также имеет возможность пропускать строки в начале файла.
for %%a in (*.csv) do (
for /f "usebackq skip=17 delims = " %%l in ("%%f") do @echo(%%l>>"%%a.new"
move /y "%%a.new" "%%a" >nul
)
Но это не сработает, если ваш файл содержит строки со специальными символами, такими как &
или |
. Для получения дополнительной информации об этом бегите for /?
Ваши пакетные решения работают с &
или |
, но единственная проблема связана с пустыми строками или строками, начинающимися с ;
Вот вариант пауэршелл; этот использует поток для обслуживания ваших больших файлов:
$csvs = Get-ChildItem -Path "P:\ath to\your csvs" -Filter *.csv
foreach ( $csv in $csvs ) {
$fin = New-Object System.IO.StreamReader( $csv.FullName )
$fout = New-Object System.IO.StreamWriter( $csv.FullName+".new" )
try {
for( $s = 1; $s -le 17 -and !$fin.EndOfStream; $s++ ) {
$fin.ReadLine()
}
while( !$fin.EndOfStream ) {
$fout.WriteLine( $fin.ReadLine() )
}
}
finally {
$fout.Close()
$fin.Close()
}
}
Просто измените путь к своим .csv
в первой строке, прежде чем тестировать его.
Я намеренно не удаляю исходные файлы, просто добавляя .new
к новым именам файлов, чтобы у вас было время проверить результаты, проверить скорость и т. д. Я оставлю вам возможность включить переименование/удаление или перемещение, если вы чувствуете необходимость расширения функционала.
В зависимости от масштаба вашего
numerous
использование простого for для итерации файлов может быть опасным, поскольку файлы могут обрабатываться несколько раз. Если это вариант, я бы использовал для этого PowerShell.