@Pastun

Как разделить текстовый файл на части с равным количеством строк?

Доброго времени суток.
Имеется текстовый файл, количество строк в исходном тексте неизвестно, может пятьдесят, а может тысыча.
Нужно разбить его на несколько частей, так, чтобы количество строк в выходных файлах было одинаково (ну ± одна строка). Количество частей задается в самом батнике.

Поскольку я плохо разбираюсь в CMD, навалял такую белиберду на пять частей:
spoiler
:movech
for %%I in (list.txt) do if %%~zI==0 (goto exit)

@echo off
setlocal enabledelayedexpansion
 
set file=list.txt
set first=1
set second=1
set out=V_Obrabotku1.txt
 
set counter=0
<nul set /p x=>>"%out%"
for /f "usebackq tokens=*" %%A IN ("%file%") DO (
 set /a counter=!counter!+1
 if !counter! GEQ %first% (
  if !counter! LEQ %second% (
   echo.%%A>>"%out%"
  )
 )
)

set n=1
set File_Src=list.txt
set file_Dest=textfile_out.txt
 
more +%n% < "%File_Src%" > "%file_Dest%"
move /y textfile_out.txt list.txt

::V_Obrabotku2

set file=list.txt
set first=1
set second=1
set out=V_Obrabotku2.txt
 
set counter=0
<nul set /p x=>>"%out%"
for /f "usebackq tokens=*" %%A IN ("%file%") DO (
 set /a counter=!counter!+1
 if !counter! GEQ %first% (
  if !counter! LEQ %second% (
   echo.%%A>>"%out%"
  )
 )
)

set n=1
set File_Src=list.txt
set file_Dest=textfile_out.txt
 
more +%n% < "%File_Src%" > "%file_Dest%"
move /y textfile_out.txt list.txt

::V_Obrabotku3

set file=list.txt
set first=1
set second=1
set out=V_Obrabotku3.txt
 
set counter=0
<nul set /p x=>>"%out%"
for /f "usebackq tokens=*" %%A IN ("%file%") DO (
 set /a counter=!counter!+1
 if !counter! GEQ %first% (
  if !counter! LEQ %second% (
   echo.%%A>>"%out%"
  )
 )
)

set n=1
set File_Src=list.txt
set file_Dest=textfile_out.txt
 
more +%n% < "%File_Src%" > "%file_Dest%"
move /y textfile_out.txt list.txt

::V_Obrabotku4

set file=list.txt
set first=1
set second=1
set out=V_Obrabotku4.txt
 
set counter=0
<nul set /p x=>>"%out%"
for /f "usebackq tokens=*" %%A IN ("%file%") DO (
 set /a counter=!counter!+1
 if !counter! GEQ %first% (
  if !counter! LEQ %second% (
   echo.%%A>>"%out%"
  )
 )
)

set n=1
set File_Src=list.txt
set file_Dest=textfile_out.txt
 
more +%n% < "%File_Src%" > "%file_Dest%"
move /y textfile_out.txt list.txt

::V_Obrabotku5

set file=list.txt
set first=1
set second=1
set out=V_Obrabotku5.txt
 
set counter=0
<nul set /p x=>>"%out%"
for /f "usebackq tokens=*" %%A IN ("%file%") DO (
 set /a counter=!counter!+1
 if !counter! GEQ %first% (
  if !counter! LEQ %second% (
   echo.%%A>>"%out%"
  )
 )
)

set n=1
set File_Src=list.txt
set file_Dest=textfile_out.txt
 
more +%n% < "%File_Src%" > "%file_Dest%"
move /y textfile_out.txt list.txt

Goto movech

:exit


Скрипт работает, но уж больно громоздкий, да и тысячу строк довольно долго обрабатывает.
Подскажите, пожалуйста, более изящное решение. Заранее спасибо.
  • Вопрос задан
  • 17641 просмотр
Пригласить эксперта
Ответы на вопрос 3
@yellowmew
Cloud infrastructure, monitoring engineer. SRE
забудьте про cmd
Powershell гораздо легче дастся вам в освоении.
пример для вашего случая
$file = get-content "путь к файлу"
$parts = 4 #количество частей
$lines = [math]::Round($file.Length/$parts) 
for ($i = 0; $i -le $parts; $i++) {
    $file | Select -Skip ($lines*$i/1) -First ($lines*($i+1)/1) | Set-Content -Path "путь к целевой папке\part_$i.txt"
}

Быстро он будет работать только в случае относительно небольших файлов. поскольку файл загружается в память полностью.
Если размер вашего файла идет на гигабайты - можете попробовать адаптировать под вас скрипт
https://stackoverflow.com/questions/1001776/how-ca...
В скрипте есть условие сравнения с размером целевых файлов - вы можете изменить его на условие сравнения с количеством строк в файле
Ответ написан
@res2001
Developer, ex-admin
Нагромоздили кода вы не меряно, все это можно сделать гораздо компактнее.
Я не стал разбираться, т.к. реально много кода для такой задачи.

Из вашего описания не ясно как именно разбивать на строки:
1.брать первые несколько строк и писать в один файл, следующую порцию в другой и т.д.
2.берем одну строку кладем в первый файл, вторую - во второй и т.д., когда заканчиваются файлы начинаем снова с первого файла.

Пункт 2 реализовывается вообще элементарно в одном цикле чтения и с одним счетчиком файлов.
Пункт 1: нужно сначала подсчитать общее количество строк (можно циклом и счетчиком, а можно поиграть с find /c /v "" <имя файла>, понятно, что find будет работать гораздо быстрее, но не совсем понятно как find будет вести себя с пустыми строками, нужно поэксперименировать) и прикинуть по сколько строк будет приходиться на каждый из файлов. Затем с помощью for /f "skip=X" - читаем файл построчно с пропуском нужного количества строк, и подсчетом скопированных строк. Так же не сильно сложно. Я думаю, после ваших героических усилий вы сможете упростить ваш код.
Будут вопросы - кидайте сюда.

PS: PowerShell, конечно, гораздо мощнее, но его синтаксис кажется мне страшней, чем у батников, возможно это по тому, что то я язык cmd знаю, а пош до сих пор нет :-)
Ответ написан
Комментировать
@Directumov
Проще в воспользоваться готовым решением, зачем тратить время на написание батника? Это решение основано на HTML5, делит файлы размером несколько гигабайт за доли секунд.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы