Windows上で巨大なテキストファイルを分割する

ソフト開発をしていると膨大なログファイルを扱うことがありますが、大きなテキストファイルは扱いが大変です。このような場合、PowerShellで以下の様にすると簡単に分割でき、扱いも楽になります。

$i=0; cat <分割対象テキストファイル> -ReadCount <分割行数> | % { $_ > <分割後テキストファイルのPrefix>$i.txt;$i++ }

ただし、CSVファイルなど先頭の数行がヘッダ行の場合だと単純な分割ではヘッダ情報がなくなるため、手動でヘッダをコピーしなくてはならず、少々使い勝手が悪いです。

この問題を解決したスクリプトファイルが以下。分割したテキストファイルのそれぞれに共通ヘッダを追加できるようにし、出力ファイルの場所を指定できるようにもしています。$lineのデフォルト値である’1048576’はExcelで扱えるCSVの最大行数になります。

SplitText.ps1:

Param(
    [String]$text_file_path,
    [int]$lines=1048576,
    [String]$output_directory,
    [int]$header
)

############
# Debug #
############
if( [string]::IsNullOrEmpty($text_file_path) ){
    $text_file_path = "S:\ScriptDevelopment\SplitText.ps1\Sample.csv"
}

if( $lines -eq 0 ){
    $lines = 1048576 #Max line number of Excel
}

if( $header -le 0 ){
    $header = 0
}

if( ![string]::IsNullOrEmpty($output_directory) ){
    if( -Not(Test-Path $output_directory) ){
        New-Item $output_directory -ItemType Directory > $null
    }
}

############
# Constant #
############
#[String]$text_file = Split-Path -Leaf ${text_file_path}
[String]$base_name = [System.IO.Path]::GetFileNameWithoutExtension(${text_file_path})
[String]$extention = [System.IO.Path]::GetExtension(${text_file_path})
[String]$file_path = Split-Path -Parent ${text_file_path}

########
# main #
########

#read header
[String]$header_lines=""
[System.IO.StreamReader]$stream_reader = New-Object System.IO.StreamReader($text_file_path)
[int]$read_count=1
while( $null -ne ($stream_line = $stream_reader.ReadLine()) ) {
    if( $read_count -gt $header ){ break }
    $header_lines += $stream_line + "`r`n"
    $read_count++
}
$stream_reader.Close()

#output
$read_count = 0;
[int]$file_number=0
[int]$max_output_lines = $lines - $header
[System.IO.StreamReader]$stream_reader = New-Object System.IO.StreamReader($text_file_path)
while( $null -ne ($stream_line = $stream_reader.ReadLine()) ) {
    if( $read_count -ge $header ){ 
        if( ( ( $read_count - $header ) % $max_output_lines ) -eq 0 ){
            if( $null -ne $stream_writer ){ $stream_writer.close() }
            $file_number++
            [string]$output_file_name=${base_name}+"_"+${file_number}+${extention}
            Write-Output ${output_file_name}
            if( [string]::IsNullOrEmpty($output_directory) ){
                [string]$output_file_path=${file_path}+"\"+${output_file_name}
            } else {
                [string]$output_file_path=${output_directory}+"\"+${output_file_name}
            }
            $stream_writer = New-Object System.IO.StreamWriter( $output_file_path, $false, [Text.Encoding]::GetEncoding("UTF-8") )
            $stream_writer.Write($header_lines)
        }
        $stream_writer.WriteLine($stream_line)
    }
    $read_count++
}
if( $null -ne $stream_reader ) { $stream_reader.Close() }
if( $null -ne $stream_writer ) { $stream_writer.Close() }

Code language: PHP (php)

以下のようなバッチファイルを作りPowershellスクリプトと同じ場所に置くと、Drag&Dropでテキストファイルを分割できます。

Split.bat:

@echo off

cd /d %~dp0

SET LINES=1048576
SET HEADER=1

for %%f in (%*) do (
    setlocal
    echo powershell -NoProfile -ExecutionPolicy Unrestricted .\SplitText.ps1 "-text_file_path \"%%f\" -lines %LINES% -header %HEADER% -output_directory \"%%~dpnf\""
    echo.
    CALL powershell -NoProfile -ExecutionPolicy Unrestricted .\SplitText.ps1 "-text_file_path \"%%f\" -lines %LINES% -header %HEADER% -output_directory \"%%~dpnf\""
    echo.
)

pause
Code language: PHP (php)