Пакетный файл для случайного удаления половины строк текстового файла?

Мне нужен способ использовать пакет для просмотра каждой строки текстового файла и удалить половину строк в этом текстовом файле, выбрав, какие строки удалить случайно.

Это должно имитировать турнир в игре D & D. Все, что мне нужно - это способ победить победителей каждого раунда турнира. Я могу легко создать пакетный файл, который копирует текстовый файл и переименовывает его для каждого раунда, но это уменьшает половину, с которой мне не нравится.

Изменить: мне нужен командный файл, потому что это займет много времени, чтобы сделать это вручную. Также игра очень старая и ролевая игра сосредоточена, поэтому в турнире есть фактический список персонажей NPC, и ПК захотят узнать, как их друзья участвовали в соревнованиях.

Ответ 1

Этот метод сохраняет порядок исходных строк.

@echo off
setlocal EnableDelayedExpansion

rem Generate an array of line numbers with all file lines
for /F "delims=:" %%a in ('findstr /N "^" input.txt') do (
   set "n[%%a]=%%a"
   set "lines=%%a"
)

rem Delete half the numbers in the array in random order
set /A halfLines=lines/2
for /L %%n in (%lines%,-1,%halfLines%) do (
   set /A rnd=!random!*%%n/32768+1
   set /A n[!rnd!]=n[%%n]
   set "n[%%n]="
)

rem Reorder the resulting elements
for /F "tokens=2,3 delims=[]=" %%i in ('set n[') do (
   set "l[%%j]=1"
   set "n[%%i]="
)

rem Copy such lines
(for /F "tokens=1* delims=:" %%a in ('findstr /N "^" input.txt') do (
   if defined l[%%a] (
      echo(%%b
      set "l[%%a]="
   )
)) > output.txt

Ответ 2

Дизайн, указанный в вопросе, является ошибочным. Вы не можете случайным образом удалить половину строк, потому что тогда для любой заданной игры вы можете получить 2 победителя или победителей. Входной файл должен иметь структуру, которая указывает, какие участники играют друг с другом, а затем победитель должен быть случайным образом выбран из каждого конкурса.

В приведенном ниже решении предполагается, что каждая строка представляет игрока, и для каждого раунда строка 1 воспроизводит строку 2, линия 3 играет строку 4 и т.д. Количество строк всегда должно быть силой 2 >= 2 (2, 4, 8, 16, 32, 64,...)

@echo off
setlocal enableDelayedExpansion
set "file=tournament.txt"
for /f %%C in ('find /c /v "" ^<"%file%"') do (
  for /l %%N in (1 2 %%C) do (
    set /p "p1="
    set /p "p2="
    set /a "1/(!random!%%2)" 2>nul&&echo !p1!||echo !p2!
  )
) <"%file%" >"%file%.new"
move /y "%file%.new" "%file%" >nul

Внешний цикл подсчитывает количество строк. Внутренний цикл подсчитывает нечетные числа от 1 до счета, поэтому он итерации точно (количество строк делится на 2) раза. Внутренний цикл имеет вход, перенаправленный в исходный файл, а выход перенаправляется во временный файл.

Каждая внутренняя итерация цикла представляет собой игру в турнире. SET/P используется для загрузки имен игроков в переменные. Затем 1 делится на случайное число по модулю 2, что приведет к делению на 0 ошибкой в ​​50% времени. Сообщение об ошибке перенаправляется на nul, а затем условные операторы используются для печати одного или другого игрока в выходной файл.

По завершении цикла временный файл перемещается для замены исходного файла.

Ответ 3

@echo off
    setlocal enableextensions disabledelayedexpansion

    set "inputFile=list.txt"
    set "outputFile=remaining.txt"

    set "odd=1"
    >"%outputFile%" (
        for /f "tokens=1,* delims=¬" %%a in ('
            "cmd /q /v /e /c" 
                for /f "usebackq delims=" %%l in ("%inputFile%"^) do (
                    set /a 100000000+%random%*^!random^!^&echo(¬%%l
                ^)
            ""
            ^| sort /+3
        ') do if not defined odd ( set "odd=1" ) else (
            echo %%b
            set "odd="
        )
    )
    type "%outputFile%"

Это займет входной файл, для каждой строки эхо его содержимое со случайным префиксом, отсортируйте этот список, используя случайное число как ключ, и из этого списка эхо выделите только нечетные строки для вывода файла.

отредактировано. Я видел ответ Aacini и, да, может быть полезно иметь вывод в том же порядке, что и вход. Если это так, просто чтобы иметь другую версию

@echo off
    setlocal enableextensions disabledelayedexpansion

    set "inputFile=list.txt"
    set "outputFile=remaining.txt"    

    setlocal enabledelayedexpansion

    rem Retrieve and calculate line limits to process
    for /f %%a in ('^<"!inputFile!" find /c /v ""') do set /a "nLines=%%a", "nLimit=%%a/2"

    rem Prepare an array with shuffled line numbers 
    for /l %%a in (1 1 %nlines%) do (
        set /a "sel=!random! %% %%a + 1"
        if !sel!==%%a ( set "r[%%a]=%%a" ) else (
            for %%s in (!sel!) do set /a "r[%%a]=!r[%%s]!", "r[%%s]=%%a"
        )
    )

    rem Read input file and output selected lines
    <"!inputFile!" >"!outputFile!" ( 
        for /l %%a in (1 1 %nLines%) do (
            set /p "line=" || set "line="
            if !r[%%a]! leq %nLimit% echo(!line!
        )
    ) 
    type "!outputFile!"

Ответ 4

Это родной пакет Windows script, используя Jscript, чтобы рандомизировать строки текстового файла и печатать половину из них.

Размер файла ограничен ОЗУ, которое Jscript может запросить.

@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScript comment
:batch file portion
@echo off
if "%~1"=="" (
echo(Purpose: Randomises a text file - prints every 2nd random line
echo(
echo(Syntax: "%~0" "inputfile.txt" ^> "outputfile.txt"
echo(
pause
goto :EOF
)

:: Randomises a file
::
:: Reads the lines of file %1 into a jscript sparse array
:: with a random number and space before each line.
:: The array is sorted using the random numbers,
:: and the randomised lines are printed - prints every 2nd random line


@echo off
cscript //E:JScript //nologo "%~f0" %* 
:pause
exit /b


************ JScript portion ***********/

  e=0;
  var array = new Array();
  fso = new ActiveXObject("Scripting.FileSystemObject");
  input=fso.OpenTextFile((WScript.Arguments(0)),1);

  while (!input.AtEndOfStream) {
    array[e]=Math.random()+" "+input.ReadLine();
    e++;
  }
  input.close();

  array.sort();

  var re = new RegExp(".*? (.*)");

  c=0;
  while (c<e) {
    var arr = re.exec(array[c])
    if (c % 2) WScript.StdOut.Writeline(RegExp.$1);
    c++;
  }

Ответ 5

Включение решения PowerShell для полноты.

$lines = Get-Content input.txt
$lines | foreach { $i=0 } {
    [PSCustomObject]@{index=$i;line=$_}
    $i++
} |
Get-Random -count ($lines.length / 2) |
sort index |
select -Expand line |
Set-Content output.txt

Это считывает входной файл и создает массив настраиваемых объектов, связывающих текст с его номером строки. script использует Get-Random для выбора половины строк. Get-Random не сохраняет порядок, поэтому script сортирует исходный номер строки. Затем он извлекает исходные строки по порядку и записывает их.

Для этого script выше требуется PowerShell 3.0 для PSCustomObject. Версия 3.0 поставляется предустановленной в Windows 7 и выше. Если вам нужно запустить Vista, вы можете использовать PSObject вместо этого, как показано в этом ответе или this сообщение в блоге.

Обратите внимание, что если порядок строк не имеет значения, то этот script становится намного проще.

$lines = Get-Content input.txt
$lines | Get-Random -count ($lines.length / 2) | Set-Content output.txt

Чтобы запустить PowerShell script из командного файла или приглашения CMD, используйте следующее.

powershell.exe -ex bypass -file yourscript.ps1

Или если ваш script полностью помещается в одну строку, нет необходимости создавать отдельный ps1

powershell.exe -c "$l = gc input.txt; $l | Get-Random -c ($l.length / 2) | sc output.txt"