【问题标题】:way to determine if file is PDF faster更快地确定文件是否为 PDF 的方法
【发布时间】:2021-07-21 20:11:11
【问题描述】:

寻找一些指针/提示来提高下面的速度和/或功效。对其他方法开放,但只涉足 powershell、cmd 和 python。

还应归功于信用:这是对以下内容的黑客工作:https://stackoverflow.com/a/44183234/12834479

我不是在本地工作,而是通过 VPN 以极低的连接速度进行网络共享。 粗略地说,它的工作时间为 8 秒/PDF。

我试图解决的问题,目标是确保每个 PDF 都可以被 Adob​​e 阅读。保存为 PDF(但不是 pdf)的图像将在某些 PDF 软件中打开,但 Adob​​e 讨厌它们。我有转换的方法,但我的速率限制器正在识别它们。

  • Adobe PDF - 以 %PDF 开头
  • 一些银行 PDF - 以“空格”开头,然后是 %PDF
  • 第三方软件 - 垃圾标题,但 %PDF 在文档中
$items = Get-ChildItem | Where-Object {$_.Extension -eq ".pdf"}
$arrary = @()
$logFile = "RESULTS_$(get-date -Format yyyymmdd).log"
$badCounter = 0
$goodCounter = 0
$msg = "`n`nProcessing " + $items.count + " files... "
Write-Host -nonewline -foregroundcolor Yellow $msg
foreach ($item in $items)
{
    trap { Write-Output "Error trapped: $_"; continue; }
    try {
    $pdfText = Get-Content $item -raw
    $ptr3 = '%PDF'
     if ('%PDF' -ne $pdfText.SubString(([System.Math]::Max(0,$pdfText.IndexOf($ptr3))),4)) { $arrary+= "$item |-failed" >>$logfile;$badCounter += 1; $badCounter} else { $goodCounter += 1; $goodCounter}
      continue;}
catch [System.Exception]{write-output "$item $_";}}
$totalCounter = $badCounter + $goodCounter

Write-Output $arrary >> $logFile
1..3 | %{ Write-Output "" >> $logFile }

Write-Output "Total: $totalCounter / BAD: $badCounter / GOOD: $goodCounter" >> $logFile
Write-Output "DONE!`n`n"

如果目前在 PS 版本 7.1.3 上运行有任何差异/但在本地也有 5.1.18。

【问题讨论】:

  • 请说您是在网络共享所在的远程计算机上执行此操作,或者至少是在它附近的服务器上执行此操作。
  • @TheMadTechnician 对我来说听起来不像“而不是在本地工作”,但是这里的魔杖是在带有 PDF 或至少相同物理网络的服务器上运行。

标签: powershell pdf .net-core adobe


【解决方案1】:

其实PDF文件根本就不是纯文本文件,而是二进制文件,所以你不应该把它们读成字符串
您要查找的内容在文件中称为 FourCC 幻数。这个四字代码可以看成Magic number来识别文件类型。 对于 PDF 文件,这 4 个字节是 0x25, 0x50, 0x44, 0x46 ("%PDF"),文件应该以这些字节开头。

对于那些真正的 PDF 文件,您可以使用以下方法进行测试:

[byte[]]$fourCC = Get-Content -Encoding Byte -ReadCount 4 -TotalCount 4 -Path 'X:\TheFile.pdf'
if ([System.Text.Encoding]::ASCII.GetString($fourCC) -ceq '%PDF') {
    Write-Host "This is a true PDF file"
}

但是,正如您所说“银行 pdf 通常以空格开头”,要同时考虑这些文件“好”,您可以这样做:

[byte[]]$sixCC = Get-Content -Encoding Byte -ReadCount 6 -TotalCount 6 -Path 'X:\TheFile.pdf'
if ([System.Text.Encoding]::ASCII.GetString($sixCC) -cmatch '%PDF') {
    Write-Host "This is a PDF file"
}

如果您还想将在文件中的任何位置找到“%PDF”的文件视为“好”,则需要将整个文件作为字符串读取,但要使用字节的一对一字节映射. 为此,您可以使用以下辅助函数:

function ConvertTo-BinaryString {
    # converts the bytes of a file to a string that has a
    # 1-to-1 mapping back to the file's original bytes.
    # Useful for performing binary regular expressions.
    Param (
        [Parameter(Mandatory = $True, ValueFromPipeline = $True, Position = 0)]
        [ValidateScript( { Test-Path $_ -PathType Leaf } )]
        [String]$Path
    )

    # Note: Codepage 28591 returns a 1-to-1 char to byte mapping
    $Encoding     = [Text.Encoding]::GetEncoding(28591)
    $Stream       = [System.IO.FileStream]::new($Path, 'Open', 'Read')
    $StreamReader = [System.IO.StreamReader]::new($Stream, $Encoding)
    $BinaryText   = $StreamReader.ReadToEnd()

    $StreamReader.Close()
    $Stream.Close()

    return $BinaryText
}

接下来,您可以将该函数用作:

$binString = ConvertTo-BinaryString -Path 'X:\TheFile.pdf'
if ($binString.IndexOf("%PDF") -ge 0) {
    Write-Host "This is a PDF file"
}

将它们放在一起并假设您希望 所有 文件标记为 .PDF 文件,其中可以在文件中的任何位置找到幻数“%PDF”(区分大小写):

function ConvertTo-BinaryString {
    # converts the bytes of a file to a string that has a
    # 1-to-1 mapping back to the file's original bytes.
    # Useful for performing binary regular expressions.
    Param (
        [Parameter(Mandatory = $True, ValueFromPipeline = $True, Position = 0)]
        [ValidateScript( { Test-Path $_ -PathType Leaf } )]
        [String]$Path
    )

    # Note: Codepage 28591 returns a 1-to-1 char to byte mapping
    $Encoding     = [Text.Encoding]::GetEncoding(28591)
    $Stream       = [System.IO.FileStream]::new($Path, 'Open', 'Read')
    $StreamReader = [System.IO.StreamReader]::new($Stream, $Encoding)
    $BinaryText   = $StreamReader.ReadToEnd()

    $StreamReader.Close()
    $Stream.Close()

    return $BinaryText
}

$badCounter  = 0
$goodCounter = 0
$logFile     = "RESULTS_{0:yyyyMMdd}.log" -f (Get-Date)

# get an array of pdf file FullNames
$files = @(Get-ChildItem -File -Filter '*.pdf').FullName
Write-Host "Processing $($files.Count) files... " -ForegroundColor Yellow
# loop through the array, test if '%PDF' is found and output strings for the log file
$result = foreach ($item in $files) {
    $pdfText = ConvertTo-BinaryString -Path $item
    if ($pdfText.IndexOf("%PDF") -ge 0) {
        $goodCounter++
        "Success - $item"
    }
    else {
        $badCounter++
        "Fail - $item"
    }
}

# write the output to the log file
$result | Set-Content -Path $logFile
"=" * 25 | Add-Content -Path $logFile
"BAD:   $badCounter"  | Add-Content -Path $logFile
"GOOD:  $goodCounter" | Add-Content -Path $logFile
"Total: $($files.Count)" | Add-Content -Path $logFile

Write-Host "DONE!" -ForegroundColor Green

【讨论】:

  • 这确实有助于加快速度,虽然不是杀手,但看起来每个文件的总时间减少了 5%。注意 for 循环中的任何其他 $file 应该是 $item。感谢您的帮助。
  • @scipio1551 感谢您发现 $file --> $item 错误。最后一分钟更改变量名..
猜你喜欢
  • 2010-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多