Разделение большого файла на количество разделителей строк CRLF - PullRequest
1 голос
/ 08 января 2020

У меня есть файл размером более 1,5 ГБ, я хочу разделить его на более мелкие куски, чтобы выполнить некоторую работу и повторно добавить после.

У меня есть скрипт ниже, который разбивает на х количество строк. Файл может содержать строки, которые представляют собой смесь разделителей только для CRLF и LF.

Я бы хотел разделить на x количество разделителей строк CRLF, поскольку в существующем сценарии я мог бы разделить два полных записи данных. CRLF является определяющим разделителем между записями, LF существуют в полях свободного текста.

Примечание: Приведенный ниже код также превращает существующий LF в CRLF. Я хотел бы сохранить разделители строк в соответствии с оригиналом.

Версия 5.1

$sourceFolder_local="D:\FileCleaning\"
$raw = $sourceFolder_local + $file.name

#split test
$sw = new-object System.Diagnostics.Stopwatch
$sw.Start()
$filename = $raw
$rootName = $raw.Replace(".csv","")
$ext = ".csv"

$linesperFile = 100000
$filecount = 1
$reader = $null
try{
    $reader = [io.file]::OpenText($filename)
    try{
        "Creating file number $filecount"
        $writer = [io.file]::CreateText("{0}{1}.{2}" -f ($rootName,$filecount.ToString("000"),$ext))
        $filecount++
        $linecount = 0

        while($reader.EndOfStream -ne $true) {
            "Reading $linesperFile"
            while( ($linecount -lt $linesperFile) -and ($reader.EndOfStream -ne $true)){
                $writer.WriteLine($reader.ReadLine());
                $linecount++
            }

            if($reader.EndOfStream -ne $true) {
                "Closing file"
                $writer.Dispose();

                "Creating file number $filecount"
                $writer = [io.file]::CreateText("{0}{1}.{2}" -f ($rootName,$filecount.ToString("000"),$ext))
                $filecount++
                $linecount = 0
            }
        }
    } finally {
        $writer.Dispose();
    }
} finally {
    $reader.Dispose();
}
$sw.Stop()

Write-Host "Split complete in " $sw.Elapsed.TotalSeconds "seconds"

Ответы [ 2 ]

1 голос
/ 22 января 2020

Я снова скачал ваш пример и проверил с помощью онлайн-редактора hex , но я все еще не могу найти LF (00 0A), которые не предшествуют с CR (00 0A). Это может быть Google Drive, но это также может быть приложение, с которым вы работаете.
Другими словами: как вы проверяете LF и / или CR?

В любом случае, поскольку это неясно Я создал свой собственный тестовый файл с LF (`n) и CRLF (строка по умолчанию заканчивается):

$TestFile = '.\Test.csv'

$Csv = "Id|Text
2146079|Line 1
3449512|Line 2 `n Line 2a
3449783|Line 3
2146079|Line 4
3449512|Line 5 `n Line 5a
3449783|Line 6 `n Line 6a
2146079|Line 7
3449512|Line 8
3449783|Line 9"

$Csv | Out-File $TestFile # -Encoding Unicode 

Хотя PowerShell может работать с классами tnet как IO.File и IO.FileStream , он имеет собственный сложный механизм потоковой передачи и собственные командлеты для этого:

$FileFolder = [IO.Path]::GetDirectoryName($TestFile)
$FileName   = [IO.Path]::GetFileNameWithoutExtension($TestFile)
$FileExt    = [IO.Path]::GetExtension($TestFile)

$LinesPerFile = 3
$FileCount = 0
$LineCount = 0

Get-Content $TestFile -Delimiter "`r`n" | ForEach-Object {
    $NewFile = !($LineCount++ % $LinesPerFile)
    If ($NewFile) {$FileCount++}
    $Path = [IO.Path]::Combine($FileFolder, ("$FileName{0:D3}.csv" -f $FileCount))
    If ($NewFile) {Set-Content -Path $Path -Value $_}
    Else          {Add-Content -Path $Path -Value $_}
}
1 голос
/ 09 января 2020

Этот скрипт разбивает файл по выбранному вами разделителю строк (например, CRLF):

Add-Type -AssemblyName System.Collections

$file          = get-item 'D:\test\largefile.txt'
$delimiter     = [environment]::NewLine      # delimiter to split file
$delimCounter  = 5                           # split after X occurances of delimiter


$fileReader   = [System.IO.StreamReader]::new( $file, [System.Text.Encoding]::default,$true)
$peek         = $fileReader.Peek()
$encoding     = $fileReader.CurrentEncoding
[void]$fileReader.Close()
[void]$fileReader.Dispose()

switch( $encoding.BodyName ) {
    'utf-8' {
        $enc = [System.Text.Encoding]::UTF8
        break
    }
    'utf-7' {
        $enc = [System.Text.Encoding]::UTF7
        break
    }
    'utf-16' {
        $enc = [System.Text.Encoding]::Unicode
        break
    }
    'utf-32' {
        $enc = [System.Text.Encoding]::UTF32
        break
    }
    'bigendianunicode' {
        $enc = [System.Text.Encoding]::BigEndianUnicode
        break
    }
    'ascii' {
        $enc = [System.Text.Encoding]::ASCII
        break
    }
    default {
        $enc = $null
    }
}

if( $enc ) {
    $delimiter = [string]::new( $enc.GetBytes($delimiter) )
}

$fileReader    = [System.IO.FileStream]::new( $file, [System.IO.FileMode]::Open )
$delimBuffer   = [System.Collections.Generic.List[byte]]::new()
$fileBuffer    = [System.Collections.Generic.List[byte]]::new()
$fileCounter   = 0
$delimCounter1 = $delimCounter

[void]$delimBuffer.AddRange( [byte[]]0 * $delimiter.Length )

$byte = $fileReader.ReadByte()

while( $byte -ge 0 ) {

    [void]$delimBuffer.RemoveAt(0)
    [void]$delimBuffer.Add( [byte]$byte )
    [void]$fileBuffer.Add( [byte]$byte )

    if( [String]::new( $delimBuffer ) -eq $delimiter ) {
        $delimCounter1--
        if( !$delimCounter1 ) {
            # remove last CRLF (if not needed, remove next line)
            [void]$fileBuffer.RemoveRange( $fileBuffer.Count - $delimiter.Length, $delimiter.Length )            
            [System.IO.File]::WriteAllBytes( ($file.DirectoryName + '\' + $file.BaseName + $fileCounter + $file.Extension), $FileBuffer ) 
            [void]$fileBuffer.Clear()
            $fileCounter++
            $delimCounter1 = $delimCounter
        }
    }

    $byte = $fileReader.ReadByte()
}

if( $fileBuffer.Count -gt 0 ) {
    [System.IO.File]::WriteAllBytes( ($file.DirectoryName + '\' + $file.BaseName + $fileCounter + $file.Extension), $fileBuffer ) 
    [void]$fileBuffer.Clear()
}

[void]$fileReader.Close()
[void]$fileReader.Dispose()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...