Розділіть текстовий файл на менший кілька текстових файлів за допомогою командного рядка


77

У мене є кілька текстових файлів із приблизно 100 000 рядків, і я хочу розділити їх на менші текстові файли по 5000 рядків кожен.

Я використав:

split -l 5000 filename.txt

Це створює файли:

xaa
xab
aac
xad
xbe
aaf

файли без розширень. Я просто хочу назвати їх приблизно так:

file01.txt
file02.txt
file03.txt
file04.txt

або якщо це неможливо, я просто хочу, щоб вони мали розширення ".txt".


2
На якій ви платформі? Ви говорите про split(утиліту Unix / Linux), але тегом batch-fileякого є Windows.
Марк Сетчелл,

1
Марк, я на вікнах, але встановив оболонку Cygwin bash, тому я маю доступ до split / csplit.
ashleybee97

@MarkSetchell Mark, так я.
ashleybee97

Ешлібі97, ти знайшов якусь відповідь
Діпак Джангір,

1
Цю відповідь за допомогою PowerShell можна вбудувати у пакетний файл. Див. Це як основу.
sancho.s ReinstateMonicaCellio

Відповіді:


93

Я знаю, що питання вже давно задавали, але я здивований, що ніхто не дав найпростішої відповіді unix:

split -l 5000 -d --additional-suffix=.txt $FileName file
  • -l 5000: розділити файл на файли по 5000 рядків кожен.
  • -d: числовий суфікс. Це змусить суфікс переходити з 00 до 99 за замовчуванням замість aa на zz.
  • --additional-suffix: дозволяє вказати суфікс, тут розширення
  • $FileName: ім'я файлу, який потрібно розділити.
  • file: префікс для додавання до отриманих файлів.

Як завжди, перевіряйте man split докладніше.

Для Mac версія за замовчуванням split, очевидно, замінена. Ви можете встановити версію GNU, використовуючи таку команду. ( див. це запитання, щоб дізнатися більше про використання GNU )

brew install coreutils

а потім ви можете запустити наведену вище команду, замінивши splitна gsplit. Ознайомтеся з man gsplitдеталями.


2
Якби я міг +100 я б! За допомогою опублікованого вами синтаксису я зміг розділити файл> 380M на 10M файлів приблизно за .3 секунди.
bakoyaro

1
Здається, -dі --additional-suffixбільше не підтримуються параметри (OSX 10.12.6)
Стефано Мунаріні

3
@StefanoMunarini для mac, ви можете встановити gnu-версію split з brew install coreutils, а потім заміните splitна gsplitза допомогою команди вище.
урсан,

і як би ви використали деліметр замість кількості рядків?
AGrush

@AGrush Я не впевнений, який саме ваш варіант використання, але я думаю, що ви могли б використовувати -tпрапор, який розділяється на роздільник, заданий користувачем, замість нового рядка. Потім ви можете використовувати -lпрапор, щоб вказати, скільки розділів ви хочете згрупувати у вихідному файлі.
урсан

22

Ось приклад у C # (бо це те, що я шукав). Мені потрібно було розділити csv-файл розміром 23 ГБ із приблизно 175 мільйонами рядків, щоб мати змогу переглядати файли. Я розділив його на файли по мільйон рядків кожен. Цей код зробив це приблизно за 5 хвилин на моїй машині:

var list = new List<string>();
var fileSuffix = 0;

using (var file = File.OpenRead(@"D:\Temp\file.csv"))
using (var reader = new StreamReader(file))
{
    while (!reader.EndOfStream)
    {
        list.Add(reader.ReadLine());

        if (list.Count >= 1000000)
        {
            File.WriteAllLines(@"D:\Temp\split" + (++fileSuffix) + ".csv", list);
            list = new List<string>();
        }
    }
}

File.WriteAllLines(@"D:\Temp\split" + (++fileSuffix) + ".csv", list);

2
І ви можете в основному просто кинути його в LINQPad і просто налаштувати до душі. Не потрібно нічого складати. Хороше рішення.
Zachary Dow

15
@ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET /a fcount=100
SET /a llimit=5000
SET /a lcount=%llimit%
FOR /f "usebackqdelims=" %%a IN ("%sourcedir%\q25249516.txt") DO (
 CALL :select
 FOR /f "tokens=1*delims==" %%b IN ('set dfile') DO IF /i "%%b"=="dfile" >>"%%c" ECHO(%%a
)
GOTO :EOF
:select
SET /a lcount+=1
IF %lcount% lss %llimit% GOTO :EOF
SET /a lcount=0
SET /a fcount+=1
SET "dfile=%sourcedir%\file%fcount:~-2%.txt"
GOTO :EOF

Ось власний пакет Windows, який повинен виконати завдання.

Тепер я не скажу, що це буде швидко (менше 2 хвилин для кожного вихідного файлу 5Kline) або що він не буде захищений від пакетних чутливостей. Дійсно залежить від характеристик ваших цільових даних.

q25249516.txtДля тестування я використав файл із іменем, що містить 100 Кліній даних.


Переглянута швидша версія

REM

@ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET /a fcount=199
SET /a llimit=5000
SET /a lcount=%llimit%
FOR /f "usebackqdelims=" %%a IN ("%sourcedir%\q25249516.txt") DO (
 CALL :select
 >>"%sourcedir%\file$$.txt" ECHO(%%a
)
SET /a lcount=%llimit%
:select
SET /a lcount+=1
IF %lcount% lss %llimit% GOTO :EOF
SET /a lcount=0
SET /a fcount+=1
MOVE /y "%sourcedir%\file$$.txt" "%sourcedir%\file%fcount:~-2%.txt" >NUL 2>nul
GOTO :EOF

Зверніть увагу, що я використовував llimit50000 для тестування. Буде чи перезаписувати перші номери файлів , якщо llimit* 100 є gearter , ніж кількість рядків у файлі (вилікувати, встановивши fcountв 1999і використовувати ~3замість ~2в рядку файлу перейменування.)


1 МБ займає 5 хв. Занадто довго
shareef

@shareef: час, який потрібно, повинен залежати від кількості рядків у файлі, а не від розміру файлу. Не впевнені, чи ви маєте на увазі лінії розміром 1 Мб або 1 Мб. Мій тест на останній версії складав 1 мільйон рядків і 11 Мб.
Magoo

Це добре, але в кінці кожного рядка залишається по одному порожньому рядку. У будь-якому випадку, щоб запобігти цьому?
Ар’я

@arya: Я не розумію "по одному порожньому рядку в кінці кожного рядка". Закінчення рядків - стандартні CRLF для Windows. У вихідних даних немає порожніх рядків. Можливо, ви використовуєте утиліту, яка зараховує як CR, так і LF як нові рядки?
Magoo

8

Ви можете зробити щось подібне за допомогою awk

awk '{outfile=sprintf("file%02d.txt",NR/5000+1);print > outfile}' yourfile

В основному, він обчислює назву вихідного файлу, беручи номер запису (NR) і ділячи його на 5000, додаючи 1, приймаючи ціле число цього і нульове відступ у 2 місця.

За замовчуванням awkдрукує весь вхідний запис, якщо ви не вказали нічого іншого. Отже, print > outfileзаписує весь вихідний запис у вихідний файл.

Оскільки ви працюєте в Windows, ви не можете використовувати одинарні лапки, тому що це не подобається. Я думаю, вам потрібно помістити сценарій у файл, а потім сказати awkвикористовувати файл, приблизно так:

awk -f script.awk yourfile

і script.awkбуде містити такий сценарій:

{outfile=sprintf("file%02d.txt",NR/5000+1);print > outfile}

Або це може спрацювати, якщо ви зробите це:

awk "{outfile=sprintf(\"file%02d.txt\",NR/5000+1);print > outfile}" yourfile

2
Це робить перший файл на один рядок менше, ніж інші. Правильна формула(NR-1)/5000+1
Давид Балажич

7

Синтаксис виглядає так:

$ split [OPTION] [INPUT [PREFIX]] 

де префікс - PREFIXaa, PREFIXab, ...

Просто використовуйте відповідний і готово, або просто використовуйте mv для перейменування. Я думаю, що це $ mv * *.txt має працювати, але спершу протестуйте його в менших масштабах.

:)


5

Моя вимога була дещо іншою. Я часто працюю з ASCII-файлами, розділеними комами та розділеними табуляцією, де один рядок - це єдиний запис даних. І вони справді великі, тому мені потрібно розділити їх на керовані частини (зберігаючи рядок заголовка).

Отже, я повернувся до свого класичного методу VBScript і об’єднав невеликий скрипт .vbs, який можна запустити на будь-якому комп’ютері Windows (він автоматично виконується двигуном хосту сценарію WScript.exe у вікні).

Перевага цього методу полягає в тому, що він використовує текстові потоки, тому базові дані не завантажуються в пам'ять (або, принаймні, не всі одночасно). Результат полягає в тому, що це надзвичайно швидко, і йому не потрібно багато пам'яті для роботи. Тестовий файл, який я щойно розбив за допомогою цього сценарію на моєму i7, був розміром близько 1 ГБ, мав близько 12 мільйонів рядків тесту і створив 25 файлів деталей (кожен з яких приблизно по 500 тисяч рядків) - обробка зайняла близько 2 хвилин, і це не зробило не перевищує 3 Мб пам'яті, що використовується в будь-який момент.

Застереження тут полягає в тому, що він покладається на текстовий файл, що має "рядки" (тобто кожен запис розмежовується CRLF), оскільки об'єкт Text Stream використовує функцію "ReadLine" для обробки окремих рядків за раз. Але привіт, якщо ви працюєте з файлами TSV або CSV, це ідеально.

Option Explicit

Private Const INPUT_TEXT_FILE = "c:\bigtextfile.txt"  'The full path to the big file
Private Const REPEAT_HEADER_ROW = True                'Set to True to duplicate the header row in each part file
Private Const LINES_PER_PART = 500000                 'The number of lines per part file

Dim oFileSystem, oInputFile, oOutputFile, iOutputFile, iLineCounter, sHeaderLine, sLine, sFileExt, sStart

sStart = Now()

sFileExt = Right(INPUT_TEXT_FILE,Len(INPUT_TEXT_FILE)-InstrRev(INPUT_TEXT_FILE,".")+1)
iLineCounter = 0
iOutputFile = 1

Set oFileSystem = CreateObject("Scripting.FileSystemObject")
Set oInputFile = oFileSystem.OpenTextFile(INPUT_TEXT_FILE, 1, False)
Set oOutputFile = oFileSystem.OpenTextFile(Replace(INPUT_TEXT_FILE, sFileExt, "_" & iOutputFile & sFileExt), 2, True)

If REPEAT_HEADER_ROW Then
    iLineCounter = 1
    sHeaderLine = oInputFile.ReadLine()
    Call oOutputFile.WriteLine(sHeaderLine)
End If

Do While Not oInputFile.AtEndOfStream
    sLine = oInputFile.ReadLine()
    Call oOutputFile.WriteLine(sLine)
    iLineCounter = iLineCounter + 1
    If iLineCounter Mod LINES_PER_PART = 0 Then
        iOutputFile = iOutputFile + 1
        Call oOutputFile.Close()
        Set oOutputFile = oFileSystem.OpenTextFile(Replace(INPUT_TEXT_FILE, sFileExt, "_" & iOutputFile & sFileExt), 2, True)
        If REPEAT_HEADER_ROW Then
            Call oOutputFile.WriteLine(sHeaderLine)
        End If
    End If
Loop

Call oInputFile.Close()
Call oOutputFile.Close()
Set oFileSystem = Nothing

Call MsgBox("Done" & vbCrLf & "Lines Processed:" & iLineCounter & vbCrLf & "Part Files: " & iOutputFile & vbCrLf & "Start Time: " & sStart & vbCrLf & "Finish Time: " & Now())


2

ось такий у c #, у якого не закінчується пам’ять при розбитті на великі фрагменти! Мені потрібно було розділити 95M файл на 10M x рядкові файли.

var fileSuffix = 0;
int lines = 0;
Stream fstream = File.OpenWrite($"{filename}.{(++fileSuffix)}");
StreamWriter sw = new StreamWriter(fstream);

using (var file = File.OpenRead(filename))
using (var reader = new StreamReader(file))
{
    while (!reader.EndOfStream)
    {
        sw.WriteLine(reader.ReadLine());
        lines++;

        if (lines >= 10000000)
        {
              sw.Close();
              fstream.Close();
              lines = 0;
              fstream = File.OpenWrite($"{filename}.{(++fileSuffix)}");
              sw = new StreamWriter(fstream);
        }
    }
}

sw.Close();
fstream.Close();

0

Я створив для цього просту програму, і ваше запитання допомогло мені завершити вирішення ... Я додав ще одну функцію і кілька конфігурацій. Якщо ви хочете додати певний символ / рядок через кожні кілька рядків (налаштовується). Будь ласка, перегляньте примітки. Я додав файли коду: https://github.com/mohitsharma779/FileSplit

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.