2015-09-05 3 views
-2

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

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

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

+0

Почему это должно быть командный файл? –

+0

Потому что я могу только программировать в пакетном режиме. –

+0

опубликуйте, что вы пробовали, код и строки, которые не работают. SO не является сайтом для написания моего программного обеспечения. –

ответ

0
@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's answer и, да, может быть полезно иметь выход в том же порядке, что и вход. Если это так, то просто иметь другую версию

@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!" 
+0

У вашего первого решения есть потенциальная проблема, что несколько запусков в пределах одной секунды будут производить одинаковый результат, потому что генератор случайных чисел высевается с текущим временем (до ближайшей секунды) при запуске CMD.EXE. – dbenham

+0

@dbenham, Если несколько экземпляров выполняются из одного и того же экземпляра 'cmd', проблем не будет. Если вы посмотрите на 'set/a 100000000 +% random% * ^! Random ^!', Вы увидите внутренний (контекст командной строки) 'cmd' использует случайное значение из родительского (пакетного) экземпляра. Если каждый из экземпляров с несколькими экземплярами (родительский, пакетный) создается в отдельном экземпляре 'cmd', то, конечно, вы абсолютно равны. –

+0

Я тестировал и замечаю, что вы правы, но я не могу окутать голову в ваш алгоритм! Решение каждого кажется мне бесполезным. Кроме того, дизайн OP ошибочен. Вы не можете случайно удалить 50% участников в турнире. Вместо этого каждая игра между двумя запланированными противниками должна иметь ровно один победитель. См. [Мой ответ] (http://stackoverflow.com/a/32438269/1012053) – dbenham

0

Это родной пакетный скрипт для Windows с использованием 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++; 
    } 
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 
+0

Мне нравится, хорошая идея сохранить порядок линий. –

0

Включая решение 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 

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

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

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

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

Для запуска сценария PowerShell из командного файла или приглашения CMD используйте следующее.

powershell.exe -ex bypass -file yourscript.ps1 

Или, если ваш сценарий подходит полностью в одной строке, нет необходимости создавать отдельный PS1

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

Простите, я не вижу логики вашего комментария «Включение комментария PowerShell для полноты». Этот вопрос имеет только те теги 'windows' и' batch-file'. Почему решение PowerShell делает ответы «полными»? Должны ли мы также включать Phyton, Ruby, PHP и т. Д. Решения «для полноты»? Другим языком программирования, который поставляется в версиях _all_ Windows, является JScript (_not_ PowerShell), поэтому включение JScript-решения в тег-вопросе 'batch-file' для полноты« кажется более логичным, чем PS ... – Aacini

+0

«Полнота» в этом случай оценивается по тому, насколько он решает проблему, которую имеет пользователь. По моему опыту, многие люди, пытающиеся создать «пакетный файл», действительно хотят, чтобы в Windows было создано решение для сценариев оболочки. PowerShell - это превосходный язык сценариев Windows, предварительно установленный на каждой (поддерживаемой) ОС, на которой работает CMD. Он решает «Я хочу сценариев Windows» лучше, чем CMD, даже если пользователь не знает, что вводить в поиск, и просто ставит «пакет». –

+0

Ваш последний комментарий основан только на _opinions_. Я хочу не начинать «по-моему», - комментирует войну, но остается только с проверенными фактами. Я попытался протестировать ваши программы, но последние две версии показали «Get-Random: Не удалось проверить аргумент сообщения« InputObject »_79 раз_ (?). Первая версия работает правильно, для обработки файла с 620 строками (19 КБ) требуется в среднем 2,02 секунды после 5 запусков под Windows 8.1 64 бит. Мое решение пакетного файла занимает 0.88 секунды при тех же условиях, поэтому PowerShell не делает (не) проблем (лучше), чем CMD (по крайней мере, не в скорости) ... – Aacini

1

Дизайн, указанный в вопросе несовершенна.Вы не можете случайным образом удалить половину строк, потому что тогда для любой заданной игры вы можете получить 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, а затем условные операторы используются для печати одного или другого игрока в выходной файл.

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

+0

Простой и очень элегантный. –

0

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

set/a number=%random% * (number of people there are)/32768 + 1 
set status=dead 
call person%number%.bat (Each file should contain a set status=live script) 
if %status% == live (
set/a peopletokill=%peopletokill%-1 
del person%number%.bat 
) 
if %peopletokill% == 0 (
exit 
) 

Нечто подобное могло бы работать.

Смежные вопросы