将二进制数据批量转换为十进制
Bulk convert binary data to decimal
有谁知道一种方法——比如 cmd 脚本——能够将 1 GB 到 10 GB 的二进制数据转换为十进制数据?
这是前 100 个字符:100011010110010011110101100001010011000101111000110110111000001100100001101011011110011010010011115=1]...[=1115=1]...[=3215=1]
1 - 我需要的是一种方法,首先 select 4 个字符串并删除那些稍后将在转换后输出 0 或大于 9 的数字。
1000 -> (8)
1101 -> (13) = delete
0110 -> (6)
0100 -> (4)
1111 -> (15) = delete
0101 -> (5)
1000 -> (8)
0101 -> (5)
0011 -> (3)
0001 -> (1)
0111 -> (7)
1000 -> (8)
1101 -> (13) = delete
1011 -> (11) = delete
1000 -> (8)
0011 -> (3)
0010 -> (2)
0001 -> (1)
1010 -> (10) = delete
1101 -> (13) = delete
1110 -> (14) = delete
0110 -> (6)
1001 -> (9)
0011 -> (3)
1111 -> (15) = delete
...
在这个过程之后,文件original_binary_data.txt不能包含输出0或大于9的数字的4个字符串,就像这样:
10000110010001011000010100110001011110001000001100100001011010010011 ...
2 - 在那一步之后,我想将二进制数据转换为十进制数据。
如果我们采用上面的示例,那么结果将类似于:
1000 -> 8
0110 -> 6
0100 -> 4
0101 -> 5
1000 -> 8
0101 -> 5
0011 -> 3
0001 -> 1
0111 -> 7
1000 -> 8
1000 -> 8
0011 -> 3
0010 -> 2
0001 -> 1
0110 -> 6
1001 -> 9
0011 -> 3
...
这应该导致文件 converted_decimal_data.txt 包含如下内容:
86458531788321693 ...
注意:二进制数据文件中除‘0’或‘1’外没有其他字符。
我需要这样做的原因是因为我需要大量的 1-9 之间的随机数据来做一个重要的实验。
您还没有发布任何您自己的代码。这似乎是一个有趣的问题,但伪装似乎可疑。在 cmd.exe 脚本中这样做是有问题的。最有可能的是,PowerShell、Python、Perl 或其他语言会更合适。
这可能不是最快的实施方式,但此 PowerShell 脚本似乎有效。
[CmdletBinding()]
param (
# The path to the file you want to read.
[Parameter(Mandatory = $true, Position = 0)]
[ValidateNotNullOrEmpty()]
[string] $InFile
,[Parameter(Mandatory = $true, Position = 1)]
[ValidateNotNullOrEmpty()]
[string] $OutFile
)
$digits = "0123456789"
if (Test-Path -Path $InFile) {
try {
$resolvedPath = Resolve-Path -Path $InFile
$fileStream = New-Object -TypeName System.IO.FileStream -ArgumentList ($resolvedPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
$fileReader = New-Object -TypeName System.IO.BinaryReader -ArgumentList $fileStream
$stream = [System.IO.StreamWriter] $OutFile
[byte[]]$abytes = $fileReader.ReadBytes(1)
while ($abytes.length -ne 0) {
[byte]$abyte = $abytes[0]
[System.Byte[]]$outbytes = @()
Write-Verbose "Got byte ===$abyte==="
$high = $abyte -shr 4
Write-Verbose "High is ===$high==="
if ($high -lt 10) { $outbytes += $digits[$high] }
$low = $abyte -band 0x0F
Write-Verbose "Low is ===$low==="
if ($low -lt 10) { $outbytes += $digits[$low] }
$stream.Write([char[]]$outbytes)
[byte[]]$abytes = $fileReader.ReadBytes(1)
}
}
catch {
Write-Warning $_.Exception.Message
}
finally {
$stream.close()
$fileReader.Dispose()
$fileStream.Dispose()
}
} else {
Write-Warning "$Path not found!"
}
将此代码保存在扩展名为 .ps1
的文件中。也许是 myconvert.ps1。然后,运行 它来自 PowerShell。
.\myconvert.ps1 .\infile.txt .\outfile.txt
如果你必须 运行 它来自 cmd.exe shell.
powershell -NoProfile -Command ".\myconvert.ps1 .\infile.txt .\outfile.txt"
编辑这是一个使用它的例子。
C:>type readnibbles.txt
abcdefghijklmnopqrstuvwxyz
C:>powershell -NoProfile -Command "Format-Hex -Path .\readnibbles.txt"
Path: C:\src\t\readnibbles.txt
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 abcdefghijklmnop
00000010 71 72 73 74 75 76 77 78 79 7A 0D 0A qrstuvwxyz..
C:>powershell -NoProfile -Command ".\readnibbles.ps1 .\readnibbles.txt .\readnibbles-out.txt"
C:>type readnibbles-out.txt
61626364656667686966666670717273747576777879700
一个经过测试且有效的 PowerShell 解决方案(比批处理解决方案快约 200 倍):
#################################################################################################################
#
# Converts 4 digit binary strings to decimal while sorting out all strings which equal 0 or are greater than 9.
#
# Adjust the source directory and input and output file names (files don't have to be .txt files).
#
$source = "C:\adjust\path"
$input_file = "file_name.extension"
$dec_output = "dec_output.txt"
$bin_output = "bin_output.txt"
#
#
# Using Get-Content on an input file with a size of 1GB or more will cause System.OutOfMemoryExceptions,
# therefore a large file gets temporarily split up.
#
$split_size = 100MB
$echo_ss = $split_size/1MB
#
#
# This adds carriage returns to the temporary file after each 16'384th character. Although the sweet spot is
# somewhere around 18'000 characters, the line length needs to be dividable by 4 and at best fit exactly n times
# into the temporary file; using 16'384 or characters (that is exactly 16 KB) ensures that.
#
$line_length = 16384
#
#
# Thanks @BenN (https://superuser.com/a/1292916/868077)
# Thanks @Bob (https://superuser.com/a/1295082/868077)
#################################################################################################################
Set-Location $source
if (Test-Path $bin_output) {
$name = (Get-Item $bin_output).Basename
$ext = (Get-Item $bin_output).Extension
$num = 1
while ($num -le 9999) {
$test = $name+"_"+$num+$ext
if (Test-Path $test) {
$num += 1
} else {
break
}
}
Rename-Item $bin_output $test
$a = "`n`n Renamed 'bin_output'!"
}
if (Test-Path $dec_output) {
$name = (Get-Item $dec_output).Basename
$ext = (Get-Item $dec_output).Extension
$num = 1
while ($num -le 9999) {
$test = $name+"_"+$num+$ext
if (Test-Path $test) {
$num += 1
} else {
break
}
}
Rename-Item $dec_output $test
$b = "`n`n Renamed 'dec_output'!"
}
if (Test-Path ".\_temp") {
"`n"
while ($overwrite -ne "true" -and $overwrite -ne "false") {
$overwrite = Read-Host ' Splitted files already/still exists! Delete and recreate?'
if ($overwrite -match "y") {
$overwrite = "true"
Remove-Item .\_temp -force -recurse
$c = " Deleted existing splitted files and creating new ones!"
} elseif ($overwrite -match "n") {
$overwrite = "false"
} elseif ($overwrite -match "c") {
exit
} else {
"`n"
Write-Host " Error: Invalid input!" "`n" " Type 'y' for 'yes'." " Type 'n' for 'no'." " Type 'c' for 'cancel'."
"`n"
"`n"
}
}
}
Clear-Host
"`n"
while ($delete -ne "true" -and $delete -ne "false") {
$delete = Read-Host ' Delete splitted files afterwards?'
if ($delete -match "y") {
$delete = "true"
$d = "`n`n Splitted files will be deleted afterwards!"
} elseif ($delete -match "n") {
$delete = "false"
$d = "`n`n Splitted files will not be deleted afterwards!"
} elseif ($delete -match "c") {
exit
} else {
"`n"
Write-Host " Error: Invalid input!" "`n" " Type 'y' for 'yes'." " Type 'n' for 'no'." " Type 'c' for 'cancel'."
"`n"
"`n"
}
}
Clear-Host
"`n"; "`n"; "`n"; "`n"; "`n"; "`n"
$a
$b
$d
$start_o = (Get-Date)
if ($overwrite -ne "false") {
$c
"`n"
$start = Get-Date
New-Item -ItemType directory -Path ".\_temp" >$null 2>&1
[Environment]::CurrentDirectory = Get-Location
$bytes = New-Object byte[] 4096
$in_file = [System.IO.File]::OpenRead($input_file)
$file_count = 0
$finished = $false
if ((Get-Item $input_file).length -gt $split_size) {
Write-Host " Input file larger than $echo_ss MB!"
Write-Host " Splitting input file and inserting carriage returns..."
$v=([MATH]::Floor([decimal]((Get-Item $input_file).Length/100MB)))
$sec_rem = -1
while (!$finished) {
$perc = [MATH]::Round($file_count/$v*100)
$file_count++
$bytes_to_read = $split_size
$out_file = New-Object System.IO.FileStream ".\_temp\_temp_$file_count.tmp",CreateNew,Write,None
while ($bytes_to_read) {
$bytes_read = $in_file.Read($bytes, 0, [Math]::Min($bytes.Length, $bytes_to_read))
if (!$bytes_read) {
$finished = $true
break
}
$bytes_to_read -= $bytes_read
$out_file.Write($bytes, 0, $bytes_read)
}
if (($i = $file_count-1) -gt 0) {
(Get-Content ".\_temp\_temp_$i.tmp") -Replace ".{$line_length}", "$&`r`n" | Set-Content ".\_temp\_temp_$i.tmp"
}
$out_file.Dispose()
$sec_elaps = (Get-Date) - $start
$sec_rem = ($sec_elaps.TotalSeconds/$file_count) * ($v-$file_count+1)
Write-Progress -Id 1 -Activity "Splitting input file and inserting carriage returns..." -Status "Progress ($perc%):" -PercentComplete ($perc) -SecondsRemaining $sec_rem
}
$in_file.Dispose()
(Get-Content ".\_temp\_temp_$file_count.tmp") -Replace ".{$line_length}", "$&`r`n" | Set-Content ".\_temp\_temp_$file_count.tmp"
Write-Progress -Id 1 -Activity null -Completed
} else {
if ((Get-Item $input_file).length -lt $split_size) {
" Input file smaller than $echo_ss MB!"
} else {
" Input file exactly $echo_ss MB!"
}
Write-Host " Inserting carriage returns..."
(Get-Content $input_file) -Replace ".{$line_length}", "$&`r`n" | Set-Content ".\_temp\_temp_1.tmp"; $file_count = 1
}
$dur = (Get-Date) - $start
Write-Host "`n Done! Duration:"$dur.ToString("hh\:mm\:ss\.fff")
"`n"
} else {
"`n"
Write-Host " Continuing with existing files..."
"`n"
Get-ChildItem ".\_temp\*" -File -Include *.tmp | ForEach-Object -Process {$file_count++}
}
Write-Host " Converting binary into decimal..."
$sec_rem = -1
$start = Get-Date
Get-ChildItem ".\_temp\*" -File -Include *.tmp | ForEach-object -Process {
$cur_file++
$line_count = (Get-Content ".\_temp\_temp_$cur_file.tmp").count
ForEach ($line in Get-Content ".\_temp\_temp_$cur_file.tmp") {
$cur_line++
$perc = [MATH]::Round(($cur_file-1+($cur_line/$line_count))/$file_count*100)
$n = 0
if ($line.length -ge 4) {
while ($n -lt $line.length) {
$dec = 0
$bin = $line.substring($n,4)
$dec = ([Convert]::ToInt32($bin,2))
if ($dec -gt 0 -and $dec -le 9) {
$temp_dec = "$temp_dec$dec"
$temp_bin = "$temp_bin$bin"
}
$n += 4
}
$temp_dec | Add-Content $dec_output -Encoding ascii -NoNewline
$temp_bin | Add-Content $bin_output -Encoding ascii -NoNewline
Clear-Variable -Name "temp_dec", "temp_bin"
}
$sec_elaps = (Get-Date) - $start
$sec_rem = ($sec_elaps.TotalSeconds/($cur_file-1+($cur_line/$line_count))) * ($file_count-($cur_file-1+($cur_line/$line_count)))
Write-Progress -ID 2 -Activity "Converting binary into decimal..." -Status "Progress ($perc%):" -PercentComplete ($perc) -SecondsRemaining $sec_rem -CurrentOperation "Current file: '_temp_$cur_file.tmp'"
}
Clear-Variable -Name "cur_line"
}
Write-Progress -Activity null -Completed
$dur = (Get-Date) - $start
Write-Host "`n Done! Duration:"$dur.ToString("hh\:mm\:ss\.fff")
"`n"
if ($delete -eq "true") {
Remove-Item ".\_temp" -Force -Recurse
}
"`n"
"`n"
Write-Host " Script finished!"
Write-Host " Start time: "$start_o.ToString("dd\.MM\.yyyy\ hh\:mm\:ss\.fff")
Write-Host " End time: "(Get-Date).ToString("dd\.MM\.yyyy\ hh\:mm\:ss\.fff")
$dur = (Get-Date) - $start_o
Write-Host "`n Duration: "$dur.ToString("hh\:mm\:ss\.fff")
"`n`n`n"
Pause
Exit
此脚本需要 1 分钟处理 10 MB 或大约 100 分钟处理 1 GB(比批处理解决方案快 200 倍)!
-------------------------------------------- ---------------------------------------------- --------
一个经过测试、有效但不实用,但批处理解决方案(比以前的批处理版本快约 3 倍):
@ECHO OFF
SETLOCAL EnableDelayedExpansion
TITLE Converting binary to decimal...
COLOR 0B
REM *********************************************
REM Set source directory!
SET "source=C:\adjust\path"
REM Set source file
SET "file_name=adjust_name.extension"
REM *********************************************
CD %source%
IF EXIST binary_output.txt SET "bin_exist=binary_output.txt " && SET "exist_and=and "
IF EXIST decimal_output.txt SET "dec_exist=decimal_output.txt" && SET "dec_exist_i=%exist_and%decimal_output.txt "
IF NOT "%bin_exist%"=="" (CALL :choice) ELSE (IF NOT "%dec_exist%"=="" CALL :choice)
CLS
powershell -Command "& {$B=$Env:file_name; (gc $B) -replace '.{4}' , """"$&`r`n"""" | sc temp.txt}"
SET time_short=%TIME:~0,2%:%TIME:~3,2%:%TIME:~6,2%
ECHO.
ECHO %time_short%:
ECHO Converting binary to decimal...
SET "startTime=%time: =0%"
FOR /F "tokens=*" %%G IN (temp.txt) DO (
SET "line=%%G"
CALL :check_line
)
CALL :log_dec
CALL :duration
DEL temp.txt >nul
ECHO.
ECHO Done^^! & ECHO. & ECHO Duration: %hh:~1%%time:~2,1%%mm:~1%%time:~2,1%%ss:~1% & ECHO.
PAUSE
EXIT
:check_line
IF "!line!"=="" EXIT /B
SET "char1=!line:~0,1!
SET "char2=!line:~1,1!
SET "char3=!line:~2,1!
SET "char4=!line:~3,1!
SET "decimal=0"
IF %char4%==1 SET /A "decimal=1"
IF %char3%==1 SET /A "decimal=%decimal%+2"
IF %char2%==1 SET /A "decimal=%decimal%+4"
IF %char1%==1 SET /A "decimal=%decimal%+8"
IF %decimal% EQU 0 EXIT /B
IF %decimal% GTR 9 EXIT /B
SET "binary_output=!binary_output!%line%"
SET "decimal_output=!decimal_output!%decimal%"
SET /A "line_number=%line_number%+1"
IF !line_number!==2043 CALL :log_bin
IF !line_number!==4086 CALL :log_bin
IF !line_number!==6129 CALL :log_bin
IF !line_number!==8172 CALL :log_dec
EXIT /B
:log_bin
SET /P "=!binary_output!" <nul >> "%source%\binary_output.txt"
SET "binary_output="
EXIT /B
:log_dec
SET /P "=!binary_output!" <nul >> "%source%\binary_output.txt"
SET /P "=!decimal_output!" <nul >> "%source%\decimal_output.txt"
SET "binary_output=" & SET "decimal_output=" & SET "line_number=0"
EXIT /B
:duration
SET "endTime=%time: =0%"
SET "end=!endTime:%time:~8,1%=%%100)*100+1!" & SET "start=!startTime:%time:~8,1%=%%100)*100+1!"
SET /A "elap=((((10!end:%time:~2,1%=%%100)*60+1!%%100)-((((10!start:%time:~2,1%=%%100)*60+1!%%100)"
SET /A "cc=elap%%100+100,elap/=100,ss=elap%%60+100,elap/=60,mm=elap%%60+100,hh=elap/60+100"
EXIT /B
:choice
ECHO. & ECHO %bin_exist%%dec_exist_i%already exists^^! & ECHO.
CHOICE /C RDC /N /M "[R]ename / [D]elete / [C]ancle"
IF ERRORLEVEL ==3 EXIT
IF ERRORLEVEL ==2 DEL %bin_exist%%dec_exist%
IF ERRORLEVEL ==1 CALL :rename
EXIT /B
:rename
IF NOT "%bin_exist%"=="" (
IF EXIST binary_output_*.txt (
FOR /F %%A IN ('DIR binary_output_*.txt /B /O:N') DO (
SET "file_name=%%~nA"
SET "file_num_1=!file_name:binary_output_=!
SET /A "file_num_1=!file_num_1!+1"
)
REN binary_output.txt binary_output_!file_num_1!.txt
) ELSE (REN binary_output.txt binary_output_1.txt)
)
IF "%dec_exist%"=="" EXIT /B
IF EXIST decimal_output_*.txt (
FOR /F %%B IN ('DIR decimal_output_*.txt /B /O:N') DO (
SET "file_name=%%~nB"
SET "file_num_2=!file_name:decimal_output_=!
SET /A "file_num_2=!file_num_2!+1"
)
REN decimal_output.txt decimal_output_!file_num_2!.txt
) ELSE (REN decimal_output.txt decimal_output_1.txt)
EXIT /B
这将创建一个可供读取的临时文件 (temp.txt
)、一个二进制输出文件 (binary_output.txt
) 和一个十进制输出文件 (decimal_output.txt
)。
脚本完成后 temp.txt
将被删除 - 或者我应该说“if 脚本完成”:
我的意思是...对于大小为 80KB 的简单 .txt
文件,此脚本只需要不到 1.5 分钟;因此,一个 1GB 的文件大约需要 315 小时 - 或 13 天!
这可能不是完美的批处理解决方案,但如果您必须转换大小为 10GB 的文件,那么即使是完美的批处理解决方案也需要数天(如果不是数周甚至数月)来处理将近 110 亿 0
和 1
的
(10GB 正好是 10'737'418'240 字节)。
我不知道你需要这个做什么,也许你有一台机器 运行 24/7/365 甚至可以转换 10GB 的文件,但如果你需要这十年左右的结果,你可能应该寻找非批处理解决方案...
但是,如果不考虑时间因素,这是一个非常有效的解决方案! :)
-------------------------------------------- ---------------------------------------------- --------
闲置时我总是使用相同的 CPU。所有时间在不同系统上可能会有很大差异!
CPU 使用: i7-4820K @3.70GHz(四核)
另外:谢谢 @BenN and @Bob for helping me out here and here!
-------------------------------------------- ---------------------------------------------- --------
编辑 (08/02/18):
添加了更快的解决方案和更小的界面。
不是将每个 4 位二进制字符串直接添加到输出文件,而是将它们添加到一个变量,直到该变量达到 8172 个字符,然后将所述变量添加到输出文件。这导致过程快了近 3 倍(上面的持续时间已经调整)!
为什么是 8172?因为 8174 是 var 的限制(在 Windows 10 上批量设置,不确定其他 Windows 版本),但不能被 4 整除,所以最后一个二进制字符串将'被完全添加到输出文件中。显然,在到达第 8172 行之前,二进制输出变量将超过其限制的 4 倍(实际上是 4 次减去 2 行),因此每 2043 行添加一次所述变量。
编辑 (12/02/18):
为 PowerShell 5.0 添加了解决方案。
我还认为,添加我拥有的 CPU 是明智的,因为这两个脚本(显然)都非常依赖 CPU 速度 and/or 内核。
编辑 (12/02/18):
添加了适用于 PowerShell 4.0 及更低版本的解决方案,因为在 PowerShell 5.0 中引入了 Out-File
的 -NoNewline
选项。
编辑 (16/02/18):
添加了对大小为 1GB 或更大的文件的支持,在我 运行 进入 System.OutOfMemoryException
之后,每当尝试对大小为 1GB 的文件使用 Get-Content
时(或更多)。
为 PowerShell 解决方案添加了一个小界面。
将 Out-File
更改为 Add-Content
,因为自 PowerShell 4.0 以来它带有一个 -NoNewline
选项,并且摆脱了 1 个额外的 PowerShell 解决方案。
编辑 (19/02/18)
解决了文件在处理前被删除的问题。
编辑 (20/02/18)
改进了界面。添加了带有估计剩余时间和转换现有文件的可能性的进度条。
已将变量 $input
更改为 $input_file
。
编辑 (20/02/18)
修复了 Write-Progress
无法处理单个文件的错误。
编辑 (03/03/18)
添加了保留拆分文件的选项。
改进了整体格式设置。
下载并安装 Python。 Python 是一种 light-weight,具有 easy-going 语法的解释性语言(一旦你通过了 Tabs 与 Space 的对比),并且当它进入 运行高效的字节码,它仍然非常快。它的解释性特性非常适合在其 built-in 控制台中进行尝试,您可以使用任何纯文本编辑器来编写和编辑更大的程序。
在创建了一个 10MB 的大型 'random' 文件之后,我以交互方式探索了您的任务需要什么。这个 7-liner 是我最终得到的。它打开一个文件用于输入,一个用于输出(它们不需要使用此语法显式 closing)并从输入中读取一批 4 个字符,直到遇到 end-of-file。 4 个二进制字符被转换为一个整数,如果它在您想要的值之间,则将其写入输出文件。
with open('original_binary_data.txt','r') as f_read:
with open('converted_decimal_data.txt','w') as f_write:
while True:
a = f_read.read(4)
if not a: break
b = int(a,2)
if b > 0 and b < 10: f_write.write(str(b))
我的 10MB 样本大约需要 5 秒的用户时间。推断 1GB 的数据¹,这将需要 100 * 5 秒或 ~ 8 分钟。
这次有可能大幅减产;读取和写入更大的缓冲区可能值得研究。但是,我想这将花费您超过 8 分钟的时间才能正确完成。如果您不希望处理 10GB 的文件(大约需要 1.5 小时),您可以花一些时间尝试一下。
或者运行这个as-is在午休时间。
¹ 好吧……假设读写比例为 O(n),这在您的(或 any)系统上可能不正确。我不会创建这么大的虚拟文件只是为了看看它是否存在。
有谁知道一种方法——比如 cmd 脚本——能够将 1 GB 到 10 GB 的二进制数据转换为十进制数据?
这是前 100 个字符:100011010110010011110101100001010011000101111000110110111000001100100001101011011110011010010011115=1]...[=1115=1]...[=3215=1]
1 - 我需要的是一种方法,首先 select 4 个字符串并删除那些稍后将在转换后输出 0 或大于 9 的数字。 在这个过程之后,文件original_binary_data.txt不能包含输出0或大于9的数字的4个字符串,就像这样: 10000110010001011000010100110001011110001000001100100001011010010011 ... 2 - 在那一步之后,我想将二进制数据转换为十进制数据。
如果我们采用上面的示例,那么结果将类似于: 这应该导致文件 converted_decimal_data.txt 包含如下内容: 86458531788321693 ... 注意:二进制数据文件中除‘0’或‘1’外没有其他字符。 我需要这样做的原因是因为我需要大量的 1-9 之间的随机数据来做一个重要的实验。1000 -> (8)
1101 -> (13) = delete
0110 -> (6)
0100 -> (4)
1111 -> (15) = delete
0101 -> (5)
1000 -> (8)
0101 -> (5)
0011 -> (3)
0001 -> (1)
0111 -> (7)
1000 -> (8)
1101 -> (13) = delete
1011 -> (11) = delete
1000 -> (8)
0011 -> (3)
0010 -> (2)
0001 -> (1)
1010 -> (10) = delete
1101 -> (13) = delete
1110 -> (14) = delete
0110 -> (6)
1001 -> (9)
0011 -> (3)
1111 -> (15) = delete
...
1000 -> 8
0110 -> 6
0100 -> 4
0101 -> 5
1000 -> 8
0101 -> 5
0011 -> 3
0001 -> 1
0111 -> 7
1000 -> 8
1000 -> 8
0011 -> 3
0010 -> 2
0001 -> 1
0110 -> 6
1001 -> 9
0011 -> 3
...
您还没有发布任何您自己的代码。这似乎是一个有趣的问题,但伪装似乎可疑。在 cmd.exe 脚本中这样做是有问题的。最有可能的是,PowerShell、Python、Perl 或其他语言会更合适。
这可能不是最快的实施方式,但此 PowerShell 脚本似乎有效。
[CmdletBinding()]
param (
# The path to the file you want to read.
[Parameter(Mandatory = $true, Position = 0)]
[ValidateNotNullOrEmpty()]
[string] $InFile
,[Parameter(Mandatory = $true, Position = 1)]
[ValidateNotNullOrEmpty()]
[string] $OutFile
)
$digits = "0123456789"
if (Test-Path -Path $InFile) {
try {
$resolvedPath = Resolve-Path -Path $InFile
$fileStream = New-Object -TypeName System.IO.FileStream -ArgumentList ($resolvedPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
$fileReader = New-Object -TypeName System.IO.BinaryReader -ArgumentList $fileStream
$stream = [System.IO.StreamWriter] $OutFile
[byte[]]$abytes = $fileReader.ReadBytes(1)
while ($abytes.length -ne 0) {
[byte]$abyte = $abytes[0]
[System.Byte[]]$outbytes = @()
Write-Verbose "Got byte ===$abyte==="
$high = $abyte -shr 4
Write-Verbose "High is ===$high==="
if ($high -lt 10) { $outbytes += $digits[$high] }
$low = $abyte -band 0x0F
Write-Verbose "Low is ===$low==="
if ($low -lt 10) { $outbytes += $digits[$low] }
$stream.Write([char[]]$outbytes)
[byte[]]$abytes = $fileReader.ReadBytes(1)
}
}
catch {
Write-Warning $_.Exception.Message
}
finally {
$stream.close()
$fileReader.Dispose()
$fileStream.Dispose()
}
} else {
Write-Warning "$Path not found!"
}
将此代码保存在扩展名为 .ps1
的文件中。也许是 myconvert.ps1。然后,运行 它来自 PowerShell。
.\myconvert.ps1 .\infile.txt .\outfile.txt
如果你必须 运行 它来自 cmd.exe shell.
powershell -NoProfile -Command ".\myconvert.ps1 .\infile.txt .\outfile.txt"
编辑这是一个使用它的例子。
C:>type readnibbles.txt
abcdefghijklmnopqrstuvwxyz
C:>powershell -NoProfile -Command "Format-Hex -Path .\readnibbles.txt"
Path: C:\src\t\readnibbles.txt
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 abcdefghijklmnop
00000010 71 72 73 74 75 76 77 78 79 7A 0D 0A qrstuvwxyz..
C:>powershell -NoProfile -Command ".\readnibbles.ps1 .\readnibbles.txt .\readnibbles-out.txt"
C:>type readnibbles-out.txt
61626364656667686966666670717273747576777879700
一个经过测试且有效的 PowerShell 解决方案(比批处理解决方案快约 200 倍):
#################################################################################################################
#
# Converts 4 digit binary strings to decimal while sorting out all strings which equal 0 or are greater than 9.
#
# Adjust the source directory and input and output file names (files don't have to be .txt files).
#
$source = "C:\adjust\path"
$input_file = "file_name.extension"
$dec_output = "dec_output.txt"
$bin_output = "bin_output.txt"
#
#
# Using Get-Content on an input file with a size of 1GB or more will cause System.OutOfMemoryExceptions,
# therefore a large file gets temporarily split up.
#
$split_size = 100MB
$echo_ss = $split_size/1MB
#
#
# This adds carriage returns to the temporary file after each 16'384th character. Although the sweet spot is
# somewhere around 18'000 characters, the line length needs to be dividable by 4 and at best fit exactly n times
# into the temporary file; using 16'384 or characters (that is exactly 16 KB) ensures that.
#
$line_length = 16384
#
#
# Thanks @BenN (https://superuser.com/a/1292916/868077)
# Thanks @Bob (https://superuser.com/a/1295082/868077)
#################################################################################################################
Set-Location $source
if (Test-Path $bin_output) {
$name = (Get-Item $bin_output).Basename
$ext = (Get-Item $bin_output).Extension
$num = 1
while ($num -le 9999) {
$test = $name+"_"+$num+$ext
if (Test-Path $test) {
$num += 1
} else {
break
}
}
Rename-Item $bin_output $test
$a = "`n`n Renamed 'bin_output'!"
}
if (Test-Path $dec_output) {
$name = (Get-Item $dec_output).Basename
$ext = (Get-Item $dec_output).Extension
$num = 1
while ($num -le 9999) {
$test = $name+"_"+$num+$ext
if (Test-Path $test) {
$num += 1
} else {
break
}
}
Rename-Item $dec_output $test
$b = "`n`n Renamed 'dec_output'!"
}
if (Test-Path ".\_temp") {
"`n"
while ($overwrite -ne "true" -and $overwrite -ne "false") {
$overwrite = Read-Host ' Splitted files already/still exists! Delete and recreate?'
if ($overwrite -match "y") {
$overwrite = "true"
Remove-Item .\_temp -force -recurse
$c = " Deleted existing splitted files and creating new ones!"
} elseif ($overwrite -match "n") {
$overwrite = "false"
} elseif ($overwrite -match "c") {
exit
} else {
"`n"
Write-Host " Error: Invalid input!" "`n" " Type 'y' for 'yes'." " Type 'n' for 'no'." " Type 'c' for 'cancel'."
"`n"
"`n"
}
}
}
Clear-Host
"`n"
while ($delete -ne "true" -and $delete -ne "false") {
$delete = Read-Host ' Delete splitted files afterwards?'
if ($delete -match "y") {
$delete = "true"
$d = "`n`n Splitted files will be deleted afterwards!"
} elseif ($delete -match "n") {
$delete = "false"
$d = "`n`n Splitted files will not be deleted afterwards!"
} elseif ($delete -match "c") {
exit
} else {
"`n"
Write-Host " Error: Invalid input!" "`n" " Type 'y' for 'yes'." " Type 'n' for 'no'." " Type 'c' for 'cancel'."
"`n"
"`n"
}
}
Clear-Host
"`n"; "`n"; "`n"; "`n"; "`n"; "`n"
$a
$b
$d
$start_o = (Get-Date)
if ($overwrite -ne "false") {
$c
"`n"
$start = Get-Date
New-Item -ItemType directory -Path ".\_temp" >$null 2>&1
[Environment]::CurrentDirectory = Get-Location
$bytes = New-Object byte[] 4096
$in_file = [System.IO.File]::OpenRead($input_file)
$file_count = 0
$finished = $false
if ((Get-Item $input_file).length -gt $split_size) {
Write-Host " Input file larger than $echo_ss MB!"
Write-Host " Splitting input file and inserting carriage returns..."
$v=([MATH]::Floor([decimal]((Get-Item $input_file).Length/100MB)))
$sec_rem = -1
while (!$finished) {
$perc = [MATH]::Round($file_count/$v*100)
$file_count++
$bytes_to_read = $split_size
$out_file = New-Object System.IO.FileStream ".\_temp\_temp_$file_count.tmp",CreateNew,Write,None
while ($bytes_to_read) {
$bytes_read = $in_file.Read($bytes, 0, [Math]::Min($bytes.Length, $bytes_to_read))
if (!$bytes_read) {
$finished = $true
break
}
$bytes_to_read -= $bytes_read
$out_file.Write($bytes, 0, $bytes_read)
}
if (($i = $file_count-1) -gt 0) {
(Get-Content ".\_temp\_temp_$i.tmp") -Replace ".{$line_length}", "$&`r`n" | Set-Content ".\_temp\_temp_$i.tmp"
}
$out_file.Dispose()
$sec_elaps = (Get-Date) - $start
$sec_rem = ($sec_elaps.TotalSeconds/$file_count) * ($v-$file_count+1)
Write-Progress -Id 1 -Activity "Splitting input file and inserting carriage returns..." -Status "Progress ($perc%):" -PercentComplete ($perc) -SecondsRemaining $sec_rem
}
$in_file.Dispose()
(Get-Content ".\_temp\_temp_$file_count.tmp") -Replace ".{$line_length}", "$&`r`n" | Set-Content ".\_temp\_temp_$file_count.tmp"
Write-Progress -Id 1 -Activity null -Completed
} else {
if ((Get-Item $input_file).length -lt $split_size) {
" Input file smaller than $echo_ss MB!"
} else {
" Input file exactly $echo_ss MB!"
}
Write-Host " Inserting carriage returns..."
(Get-Content $input_file) -Replace ".{$line_length}", "$&`r`n" | Set-Content ".\_temp\_temp_1.tmp"; $file_count = 1
}
$dur = (Get-Date) - $start
Write-Host "`n Done! Duration:"$dur.ToString("hh\:mm\:ss\.fff")
"`n"
} else {
"`n"
Write-Host " Continuing with existing files..."
"`n"
Get-ChildItem ".\_temp\*" -File -Include *.tmp | ForEach-Object -Process {$file_count++}
}
Write-Host " Converting binary into decimal..."
$sec_rem = -1
$start = Get-Date
Get-ChildItem ".\_temp\*" -File -Include *.tmp | ForEach-object -Process {
$cur_file++
$line_count = (Get-Content ".\_temp\_temp_$cur_file.tmp").count
ForEach ($line in Get-Content ".\_temp\_temp_$cur_file.tmp") {
$cur_line++
$perc = [MATH]::Round(($cur_file-1+($cur_line/$line_count))/$file_count*100)
$n = 0
if ($line.length -ge 4) {
while ($n -lt $line.length) {
$dec = 0
$bin = $line.substring($n,4)
$dec = ([Convert]::ToInt32($bin,2))
if ($dec -gt 0 -and $dec -le 9) {
$temp_dec = "$temp_dec$dec"
$temp_bin = "$temp_bin$bin"
}
$n += 4
}
$temp_dec | Add-Content $dec_output -Encoding ascii -NoNewline
$temp_bin | Add-Content $bin_output -Encoding ascii -NoNewline
Clear-Variable -Name "temp_dec", "temp_bin"
}
$sec_elaps = (Get-Date) - $start
$sec_rem = ($sec_elaps.TotalSeconds/($cur_file-1+($cur_line/$line_count))) * ($file_count-($cur_file-1+($cur_line/$line_count)))
Write-Progress -ID 2 -Activity "Converting binary into decimal..." -Status "Progress ($perc%):" -PercentComplete ($perc) -SecondsRemaining $sec_rem -CurrentOperation "Current file: '_temp_$cur_file.tmp'"
}
Clear-Variable -Name "cur_line"
}
Write-Progress -Activity null -Completed
$dur = (Get-Date) - $start
Write-Host "`n Done! Duration:"$dur.ToString("hh\:mm\:ss\.fff")
"`n"
if ($delete -eq "true") {
Remove-Item ".\_temp" -Force -Recurse
}
"`n"
"`n"
Write-Host " Script finished!"
Write-Host " Start time: "$start_o.ToString("dd\.MM\.yyyy\ hh\:mm\:ss\.fff")
Write-Host " End time: "(Get-Date).ToString("dd\.MM\.yyyy\ hh\:mm\:ss\.fff")
$dur = (Get-Date) - $start_o
Write-Host "`n Duration: "$dur.ToString("hh\:mm\:ss\.fff")
"`n`n`n"
Pause
Exit
此脚本需要 1 分钟处理 10 MB 或大约 100 分钟处理 1 GB(比批处理解决方案快 200 倍)!
-------------------------------------------- ---------------------------------------------- --------
一个经过测试、有效但不实用,但批处理解决方案(比以前的批处理版本快约 3 倍):
@ECHO OFF
SETLOCAL EnableDelayedExpansion
TITLE Converting binary to decimal...
COLOR 0B
REM *********************************************
REM Set source directory!
SET "source=C:\adjust\path"
REM Set source file
SET "file_name=adjust_name.extension"
REM *********************************************
CD %source%
IF EXIST binary_output.txt SET "bin_exist=binary_output.txt " && SET "exist_and=and "
IF EXIST decimal_output.txt SET "dec_exist=decimal_output.txt" && SET "dec_exist_i=%exist_and%decimal_output.txt "
IF NOT "%bin_exist%"=="" (CALL :choice) ELSE (IF NOT "%dec_exist%"=="" CALL :choice)
CLS
powershell -Command "& {$B=$Env:file_name; (gc $B) -replace '.{4}' , """"$&`r`n"""" | sc temp.txt}"
SET time_short=%TIME:~0,2%:%TIME:~3,2%:%TIME:~6,2%
ECHO.
ECHO %time_short%:
ECHO Converting binary to decimal...
SET "startTime=%time: =0%"
FOR /F "tokens=*" %%G IN (temp.txt) DO (
SET "line=%%G"
CALL :check_line
)
CALL :log_dec
CALL :duration
DEL temp.txt >nul
ECHO.
ECHO Done^^! & ECHO. & ECHO Duration: %hh:~1%%time:~2,1%%mm:~1%%time:~2,1%%ss:~1% & ECHO.
PAUSE
EXIT
:check_line
IF "!line!"=="" EXIT /B
SET "char1=!line:~0,1!
SET "char2=!line:~1,1!
SET "char3=!line:~2,1!
SET "char4=!line:~3,1!
SET "decimal=0"
IF %char4%==1 SET /A "decimal=1"
IF %char3%==1 SET /A "decimal=%decimal%+2"
IF %char2%==1 SET /A "decimal=%decimal%+4"
IF %char1%==1 SET /A "decimal=%decimal%+8"
IF %decimal% EQU 0 EXIT /B
IF %decimal% GTR 9 EXIT /B
SET "binary_output=!binary_output!%line%"
SET "decimal_output=!decimal_output!%decimal%"
SET /A "line_number=%line_number%+1"
IF !line_number!==2043 CALL :log_bin
IF !line_number!==4086 CALL :log_bin
IF !line_number!==6129 CALL :log_bin
IF !line_number!==8172 CALL :log_dec
EXIT /B
:log_bin
SET /P "=!binary_output!" <nul >> "%source%\binary_output.txt"
SET "binary_output="
EXIT /B
:log_dec
SET /P "=!binary_output!" <nul >> "%source%\binary_output.txt"
SET /P "=!decimal_output!" <nul >> "%source%\decimal_output.txt"
SET "binary_output=" & SET "decimal_output=" & SET "line_number=0"
EXIT /B
:duration
SET "endTime=%time: =0%"
SET "end=!endTime:%time:~8,1%=%%100)*100+1!" & SET "start=!startTime:%time:~8,1%=%%100)*100+1!"
SET /A "elap=((((10!end:%time:~2,1%=%%100)*60+1!%%100)-((((10!start:%time:~2,1%=%%100)*60+1!%%100)"
SET /A "cc=elap%%100+100,elap/=100,ss=elap%%60+100,elap/=60,mm=elap%%60+100,hh=elap/60+100"
EXIT /B
:choice
ECHO. & ECHO %bin_exist%%dec_exist_i%already exists^^! & ECHO.
CHOICE /C RDC /N /M "[R]ename / [D]elete / [C]ancle"
IF ERRORLEVEL ==3 EXIT
IF ERRORLEVEL ==2 DEL %bin_exist%%dec_exist%
IF ERRORLEVEL ==1 CALL :rename
EXIT /B
:rename
IF NOT "%bin_exist%"=="" (
IF EXIST binary_output_*.txt (
FOR /F %%A IN ('DIR binary_output_*.txt /B /O:N') DO (
SET "file_name=%%~nA"
SET "file_num_1=!file_name:binary_output_=!
SET /A "file_num_1=!file_num_1!+1"
)
REN binary_output.txt binary_output_!file_num_1!.txt
) ELSE (REN binary_output.txt binary_output_1.txt)
)
IF "%dec_exist%"=="" EXIT /B
IF EXIST decimal_output_*.txt (
FOR /F %%B IN ('DIR decimal_output_*.txt /B /O:N') DO (
SET "file_name=%%~nB"
SET "file_num_2=!file_name:decimal_output_=!
SET /A "file_num_2=!file_num_2!+1"
)
REN decimal_output.txt decimal_output_!file_num_2!.txt
) ELSE (REN decimal_output.txt decimal_output_1.txt)
EXIT /B
这将创建一个可供读取的临时文件 (temp.txt
)、一个二进制输出文件 (binary_output.txt
) 和一个十进制输出文件 (decimal_output.txt
)。
脚本完成后 temp.txt
将被删除 - 或者我应该说“if 脚本完成”:
我的意思是...对于大小为 80KB 的简单 .txt
文件,此脚本只需要不到 1.5 分钟;因此,一个 1GB 的文件大约需要 315 小时 - 或 13 天!
这可能不是完美的批处理解决方案,但如果您必须转换大小为 10GB 的文件,那么即使是完美的批处理解决方案也需要数天(如果不是数周甚至数月)来处理将近 110 亿 0
和 1
的
(10GB 正好是 10'737'418'240 字节)。
我不知道你需要这个做什么,也许你有一台机器 运行 24/7/365 甚至可以转换 10GB 的文件,但如果你需要这十年左右的结果,你可能应该寻找非批处理解决方案...
但是,如果不考虑时间因素,这是一个非常有效的解决方案! :)
-------------------------------------------- ---------------------------------------------- --------
闲置时我总是使用相同的 CPU。所有时间在不同系统上可能会有很大差异!
CPU 使用: i7-4820K @3.70GHz(四核)
另外:谢谢 @BenN and @Bob for helping me out here and here!
-------------------------------------------- ---------------------------------------------- --------
编辑 (08/02/18):
添加了更快的解决方案和更小的界面。
不是将每个 4 位二进制字符串直接添加到输出文件,而是将它们添加到一个变量,直到该变量达到 8172 个字符,然后将所述变量添加到输出文件。这导致过程快了近 3 倍(上面的持续时间已经调整)!
为什么是 8172?因为 8174 是 var 的限制(在 Windows 10 上批量设置,不确定其他 Windows 版本),但不能被 4 整除,所以最后一个二进制字符串将'被完全添加到输出文件中。显然,在到达第 8172 行之前,二进制输出变量将超过其限制的 4 倍(实际上是 4 次减去 2 行),因此每 2043 行添加一次所述变量。
编辑 (12/02/18):
为 PowerShell 5.0 添加了解决方案。
我还认为,添加我拥有的 CPU 是明智的,因为这两个脚本(显然)都非常依赖 CPU 速度 and/or 内核。
编辑 (12/02/18):
添加了适用于 PowerShell 4.0 及更低版本的解决方案,因为在 PowerShell 5.0 中引入了 Out-File
的 -NoNewline
选项。
编辑 (16/02/18):
添加了对大小为 1GB 或更大的文件的支持,在我 运行 进入 System.OutOfMemoryException
之后,每当尝试对大小为 1GB 的文件使用 Get-Content
时(或更多)。
为 PowerShell 解决方案添加了一个小界面。
将 Out-File
更改为 Add-Content
,因为自 PowerShell 4.0 以来它带有一个 -NoNewline
选项,并且摆脱了 1 个额外的 PowerShell 解决方案。
编辑 (19/02/18)
解决了文件在处理前被删除的问题。
编辑 (20/02/18)
改进了界面。添加了带有估计剩余时间和转换现有文件的可能性的进度条。
已将变量 $input
更改为 $input_file
。
编辑 (20/02/18)
修复了 Write-Progress
无法处理单个文件的错误。
编辑 (03/03/18)
添加了保留拆分文件的选项。
改进了整体格式设置。
下载并安装 Python。 Python 是一种 light-weight,具有 easy-going 语法的解释性语言(一旦你通过了 Tabs 与 Space 的对比),并且当它进入 运行高效的字节码,它仍然非常快。它的解释性特性非常适合在其 built-in 控制台中进行尝试,您可以使用任何纯文本编辑器来编写和编辑更大的程序。
在创建了一个 10MB 的大型 'random' 文件之后,我以交互方式探索了您的任务需要什么。这个 7-liner 是我最终得到的。它打开一个文件用于输入,一个用于输出(它们不需要使用此语法显式 closing)并从输入中读取一批 4 个字符,直到遇到 end-of-file。 4 个二进制字符被转换为一个整数,如果它在您想要的值之间,则将其写入输出文件。
with open('original_binary_data.txt','r') as f_read:
with open('converted_decimal_data.txt','w') as f_write:
while True:
a = f_read.read(4)
if not a: break
b = int(a,2)
if b > 0 and b < 10: f_write.write(str(b))
我的 10MB 样本大约需要 5 秒的用户时间。推断 1GB 的数据¹,这将需要 100 * 5 秒或 ~ 8 分钟。
这次有可能大幅减产;读取和写入更大的缓冲区可能值得研究。但是,我想这将花费您超过 8 分钟的时间才能正确完成。如果您不希望处理 10GB 的文件(大约需要 1.5 小时),您可以花一些时间尝试一下。
或者运行这个as-is在午休时间。
¹ 好吧……假设读写比例为 O(n),这在您的(或 any)系统上可能不正确。我不会创建这么大的虚拟文件只是为了看看它是否存在。