从文本文件中随机选择一行,并将该行设置为变量

时间:2016-01-16 15:17:27

标签: batch-file

如何从文本文件中随机选择一行/ ip,并将其设置为变量。

我的意思是:

set IP=TheLine/IPFromOfTheFile

6 个答案:

答案 0 :(得分:3)

基于阵列的解决方案似乎应该是最快的,因为它们只读取一次文件。但是当环境变得非常大时,变量操作变得非常慢。因此,基于阵列的解决方案可提供非线性性能。如果文件非常大,它们可能会非常慢。

对于基于纯批处理的解决方案,无论文件大小如何,都能提供良好的线性性能,我喜欢aschipfl's answer。但是,由于/ f EOL值的默认值,以;开头的行存在问题。如果文件为空,它也会失败。这是一个几乎完美的修正版本。

@echo off
setlocal
set "file=test.txt"
set "LINE="
for /F %%N in (
  'type "%file%" ^| find /C /V ""'
) do set set /A RND=%RANDOM% %% %%N 2>nul || goto :BREAK
if %RND%>0 (set "skip=skip^=%RND%") else set "skip="
for /F usebackq^ %skip%^ delims^=^ eol^= %%L in ("%file%") do (
  set "LINE=%%L"
  goto :BREAK
)
:BREAK
set LINE

上面还有一个缺陷 - 如果所选行为空,那么由于FOR / F的设计,它将返回下一个非空行。

"标准"解决方法是使用FINDSTR为每行添加行号,然后使用扩展find / replace来删除前缀。鉴于行号前缀的存在,我喜欢使用额外的FINDSTR来选择正确的行,而不是依赖于FOR / F

@echo off
setlocal
set "file=test.txt"
set "ln="
for /f %%N in (
  'type "%file%" ^| find /c /v ""'
) do set /a rnd=%random% %% %%N + 1 2>nul || goto :result
for /f "delims=" %%L in (
  'findstr /n "^" "%file%" ^| findstr "^%rnd%:"'
) do set "ln=%%L"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
:result
set ln

如果您知道所有行都是< = 1021字节长,那么有一个更简单的解决方案来处理使用SET / P而不是FOR / F读取文件的空行。

@echo off
setlocal
set "file=\utils\jrepl.bat"
set "ln="
for /f %%N in (
  'type "%file%" ^| find /c /v ""'
) do set /a rnd=%random% %% %%N 2>nul || goto :result
<"%file%" (
  for /l %%N in (1 1 %rnd%) do set /p "ln="
  set "ln="
  set /p "ln="
)
:result
set ln

只是为了好运,我决定根据我的JREPL.BAT regular expression text processing utility编写一个解决方案。可以编写一个优化的混合JScript /批处理脚本,但如果你已经把它放在你的技巧包中,那么JREPL很方便。

如果您只需要打印一条随机线,那么它就是一个简单的衬垫:

call jrepl "^.*" $0 /c /jmatch /jbeg "rnd=Math.floor(Math.random()*cnt)+1" /jbegln "skip=(rnd!=ln)" /f test.txt

需要更多代码才能获得变量中的值:

@echo off
setlocal
set "ln="
for /f "delims=" %%L in (
  'jrepl "^.*" $0 /c /n 1 /jmatch /jbeg "rnd=Math.floor(Math.random()*cnt)+1" /jbegln "skip=(rnd!=ln)" /f test.txt'
) do set "ln=%%L"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
:result
set ln

如果这些行都是&lt; = 1021并且您不介意临时文件,那么您可以使用SET / P

@echo off
setlocal
call jrepl "^.*" $0 /c /jmatch /jbeg "rnd=Math.floor(Math.random()*cnt)+1" /jbegln "skip=(rnd!=ln)" /f test.txt /o temp.txt
set "ln="
<temp.txt set /p "ln="
del temp.txt
:result
set ln

答案 1 :(得分:2)

首先,您需要知道文本文件textfile.txt中出现的行数:

for /F %%N in ('type "textfile.txt" ^| find /C /V ""') do set NUM=%%N

然后计算从0NUM-1的范围内的随机数:

set /A RND=%RANDOM%%%NUM

最后从文本文件中选择一行并将其存储在LINE中。 RND保存for /F循环要跳过的行数:

if %RND% EQU 0 (set "SKIP=") else (set "SKIP=skip=%RND% ")
for /F "usebackq %SKIP%delims=" %%L in ("textfile.txt") do (
    set "LINE=%%L"
    goto :NEXT
)
:NEXT

这依赖于文本文件不包含任何空行的事实。

答案 2 :(得分:1)

@echo off
setlocal EnableDelayedExpansion

rem Load file lines into "line" array
set "num=0"
for /F "usebackq delims=" %%a in ("theFile.txt") do (
   set /A "num+=1"
   set "line[!num!]=%%a"
)

rem Select one random line
set /A "rnd=%random% %% num + 1"
set "IP=!line[%rnd%]!"

echo %IP%

有关批处理文件中阵列管理的更多详细信息,请参阅this post

答案 3 :(得分:1)

如果我早些时候接受过这个问题,我可能会写出Aacini的答案。他从我那里获得+1。

无论如何,对于一些不同的东西怎么样?这是一个调用PowerShell帮助程序的批处理单行程序。

for /f "delims=" %%I in ('powershell "get-random (gc \"%file%\")"') do set "IP=%%I"

它比Aacini的解决方案慢,但它确实具有简单的优势。 *耸肩*如果你的IP文件包含任何空行,你可以添加一个选择器,只包括真实性不评估为false / empty的行。

for /f "delims=" %%I in ('powershell "gc \"%file%\" | ?{$_} | get-random"') do set "IP=%%I"

我还考虑过使用find /c /v "" txtfile然后使用for /f "skip=%randomLineNumber%"来获取文本文件中的行数(就像aschipfl的回答一样),但是效率已经低于其他答案了提供。

答案 4 :(得分:0)

试试这个:

@echo off
setlocal EnableDelayedExpansion
set "file=somefile.txt"
set "lines=0"
for /F "usebackq delims=" %%a in ("%file%") do set /a "lines+=1"
set /a "selected=%random%%%%lines%"
set "lines=0"
for /F "usebackq delims=" %%a in ("%file%") do (
if !lines! equ !selected! set "line=%%a"
set /a "lines+=1"
)
echo %line%
pause

答案 5 :(得分:-2)

我的解决方案基本上与Aacini相同,但有一些不同的细节:

@echo off

setlocal enabledelayedexpansion
set i=0
for /f "tokens=*" %%x in (%1) do (
    set line[!i!]=%%x
    set /a i += 1
)
set /a j=%random% %% %i%
set ip=!line[%j%]!

文件名是此批次的参数。