Обмен строк пакетного файла по номеру строки - PullRequest
0 голосов
/ 07 февраля 2019

Я написал пакетную программу, которая обменивается строкой из файла .txt.У меня есть два .txt файла:

Первый, test1.txt содержит запись, которая выглядит следующим образом:

1:hello
2:how
3:are
4:you

Второй файл .txt (test2.txt) содержитстрока с номером строки выглядит следующим образом:

3:good

Я бы хотел заменить третью строку из test1.txt на строку в text2.txt.Я пробовал следующую партию, но получаю только сообщения об ошибках:

setlocal EnableDelayedExpansion

for /F "tokens=1 delims=:" %%a in (test2.txt) do (
    set lng=%%a
)   

for /F "tokens=1 delims=" %%a in (test2.txt) do (
        set lnh=%%a
)   

set lineNumberExchange=%lng%
set lineNew=%lnh%

if exist beispiel.tmp del beispiel.tmp

set lineNr=0

for /f "delims=" %%A in (test1.txt) do (
    set /a lineNr+=1 >NUL
    if !lineNr!==%lineNumberExchange% ( 
        echo %lineNew%>>beispiel.tmp
    ) 
    else ( 
        echo %%A>>beispiel.tmp
    )
)

Есть идеи?

Ответы [ 5 ]

0 голосов
/ 08 февраля 2019

Вот более общая версия, которая позволяет файлу замены (test2.txt) содержать более одной строки, исходя из следующих предположений:

  • длины строк обоих входных файлов делаютне более 1021 символа / байт, включая префикс номера строки;
  • файл для изменения (test1.txt) содержит менее 2 31 строк;
  • номеров строк файла дляизменение должно быть непрерывным и начинаться с единицы;
  • номера строк файла замены должны быть отсортированы в порядке возрастания, дубликатов не возникает;

Это код скрипта, давайтеназовите его replace-numbered-lines.bat:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=%~f1" & rem // (file whose lines are to be replaced)
set "_REPL=%~f2" & rem // (file that contains replacement lines)
set "_RETF=%~f3" & rem // (return file)

rem /* Build temporary file path (the temporary file allows the return file to be
rem    the same as an input file): */
:LOOP_TEMP
for %%F in ("%TEMP%\%~n0_%RANDOM%.tmp") do set "TMPF=%%~fF"
if /I "%TMPF%"=="%_FILE%" goto :LOOP_TEMP
if /I "%TMPF%"=="%_RETF%" goto :LOOP_TEMP

rem // Determine number of lines of file to modify:
for /F %%C in ('^< "%_FILE%" find /C /V ""') do set "FCNT=%%C"

rem // Open both files for being read:
9< "%_FILE%" 8< "%_REPL%" > "%TMPF%" (
    rem // Initialise replacement line number:
    set /A "RNUM=0"
    setlocal EnableDelayedExpansion
    rem // Loop through the lines of the file to modify:
    for /L %%I in (1,1,%FCNT%) do (
        rem // Read a line from the file to modify:
        <&9 (set "LINE=" & set /P LINE="")
        rem // Extract the line number:
        for /F "delims=:" %%N in ("!LINE!") do set /A "FNUM=%%N"
        rem /* Check whether buffer for line number of replacement file is defined;
        rem    if it is non-empty, there are still unread lines; if it is empty, the
        rem    end of the replacement file has already been reached: */
        if defined RNUM (
            rem /* Check whether line number from file to modify lies beyond the
            rem    buffered line of the of replacement file: */
            if !RNUM! lss !FNUM! (
                rem // Clear buffer for line number of replacement file:
                set "RNUM="
                rem // Attempt to read another line from the replacement file:
                <&8 (set "LNEW=" & set /P LNEW="")
                rem // Extract the line number (if the line is not empty):
                for /F "delims=:" %%N in ("!LNEW!") do set /A "RNUM=%%N"
            )
            rem /* Check whether the line number of the file to modify equals the
            rem    current (new or buffered) line number of the replacement file: */
            if !RNUM! equ !FNUM! (
                rem // Line numbers equal, so replace the current line:
                echo(!LNEW!
            ) else (
                rem // Line numbers do not equal, so keep current line:
                echo(!LINE!
            )
        ) else (
            rem // Keep current line since end of replacement file is reached:
            echo(!LINE!
        )
    )
    endlocal
)

rem // Return resulting file content:
if defined _RETF (
    rem // Move temporary file onto return file, if there is such:
    > nul move /Y "%TMPF%" "%_RETF%"
) else (
    rem // Display result on console and delete temporary file:
    type "%TMPF%"
    del "%TMPF%"
)

endlocal
exit /B

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

Чтобы применить его к вашим входным файлам, вызовите егопо следующей командной строке:

replace-numbered-lines.bat test1.txt test2.txt

Это отобразит результат в консоли.Чтобы записать его в другой файл, используйте это:

replace-numbered-lines.bat test1.txt test2.txt test3.txt

Выходной файл может даже быть одним из входных файлов:

replace-numbered-lines.bat test1.txt test2.txt test1.txt

Если файл замены (test2.txt) может содержать несортированные номера строк (без дубликатов), может работать следующий подход (учитывая, что все остальные допущения из вышеупомянутого все еще применяются):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=%~f1" & rem // (file whose lines are to be replaced)
set "_REPL=%~f2" & rem // (file that contains replacement lines)
set "_RETF=%~f3" & rem // (return file)

rem /* Build temporary file path (the temporary file allows the return file to be
rem    the same as an input file): */
:LOOP_TEMP
for %%F in ("%TEMP%\%~n0_%RANDOM%.tmp") do set "TMPF=%%~fF"
if /I "%TMPF%"=="%_FILE%" goto :LOOP_TEMP
if /I "%TMPF%"=="%_RETF%" goto :LOOP_TEMP

rem // Determine number of lines of file to modify:
for /F %%C in ('^< "%_FILE%" find /C /V ""') do set "FCNT=%%C"

rem // Open both files for being read:
< "%_FILE%" > "%TMPF%" (
    setlocal EnableDelayedExpansion
    rem // Loop through the lines of the file to modify:
    for /L %%I in (1,1,%FCNT%) do (
        rem // Read a line from the file to modify:
        set "LINE=" & set /P LINE=""
        rem // Extract the line number:
        for /F "delims=:" %%N in ("!LINE!") do set /A "FNUM=%%N"
        rem /* Find line in replacement file with the same line number prefix as
        rem    the file to modify; if found, just return it; if not, then return
        rem    original line from file to modify: */
        2> nul findstr /B "!FNUM!:" "!_REPL!" || echo(!LINE!
    )
    endlocal
)

rem // Return resulting file content:
if defined _RETF (
    rem // Move temporary file onto return file, if there is such:
    > nul move /Y "%TMPF%" "%_RETF%"
) else (
    rem // Display result on console and delete temporary file:
    type "%TMPF%"
    del "%TMPF%"
)

endlocal
exit /B

Этот сценарий проще и короче, но в целомпроизводительность должна быть хуже, потому что файл замены читается один раз в каждой строке файла для изменения (test1.txt).

0 голосов
/ 08 февраля 2019

Вот как бы я это сделал:

@echo off
selocal EnableDelayedExpansion

rem Load original lines from test1.txt
for /F "tokens=1* delims=:" %%a in (test1.txt) do (
   set "line[%%a]=%%b"
   set "last=%%a"
)

rem Replace same-numbered lines from test2.txt
for /F "tokens=1* delims=:" %%a in (test2.txt) do set "line[%%a]=%%b"

rem Output final lines
(for /L %%i in (1,1,%last%) do echo %%i:!line[%%i]!) > test1.txt
0 голосов
/ 07 февраля 2019

IIUR файл test2.txt содержит номер строки для замены и текст замены, разделенный двоеточием.

Я прочитал бы замены в массив и проверил бы, существует ли он при итерации файла text1.txt

Таким образом, вы можете иметь несколько замен (а также более длинный текст)

С учетом этих файлов примеров:

> type test?.txt

test1.txt

1:hello
2:how
3:are
4:you
5:

test2.txt

3:good are
5:at coding batches

этот пакет:

:: Q:\Test\2019\02\07\SO_54576635.cmd
@Echo off&SetLocal EnableDelayedExpansion

:: read in test2.txt into array Replace[#]=content 
for /f "usebackq tokens=1* delims=:" %%A in ("test2.txt") Do Set "Replace[%%A]=%%B"

for /f "usebackq delims=" %%L in ("test1.txt") Do (
    for /f "tokens=1* delims=:" %%A in ("%%L") Do (
        If defined Replace[%%A] (
            Echo:%%A:!Replace[%%A]!
        ) else (
            Echo:%%L
        )
    )
)

дает этот вывод:

> Q:\Test\2019\02\07\SO_54576635.cmd
1:hello
2:how
3:good are
4:you
5:at coding batches
0 голосов
/ 07 февраля 2019

С test1.txt и test2.txt, как в вашем вопросе, вы можете получить это как test.cmd:

@(Set/P "_=")<"test2.txt"&Set/A #=_ 2>Nul
@(For /F "UseBackQTokens=1*Delims=:" %%A In ("test1.txt") Do @If Not "%%A"=="%#%" (Echo(%%A:%%B)Else Echo(%_%)>"test3.txt"

Это должно вывести то, что вы хотите test3.txt.

0 голосов
/ 07 февраля 2019

Я бы предложил следующий код для этой задачи:

@echo off
setlocal EnableDelayedExpansion

set "line_number=3"

for /F "tokens=2 delims=:" %%A IN ('type "test2.txt" ^| findstr /c:%line_number%') do (
    set "replace=%%A"
)

for /F "delims=" %%B IN ('type "test1.txt" ^& del "test1.txt"') do (
    set "line=%%B"
    if not "!line:%line_number%=!" == "!line!" (
        (echo %line_number%:%replace%)>>"test1.txt"
    ) else (
        (echo !line!)>>"test1.txt"
    )
)

Сначала создайте цикл, чтобы найти слово замены, а затем замените его другим циклом.

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

Если предположить, что выше, следующий код работает:

@echo off
setlocal EnableDelayedExpansion

set "line_counter=0"
set "line_number=3"

for /F "tokens=2 delims=:" %%A IN (test2.txt) do (
    set "replace=%%A"
)

for /F "delims=" %%B IN ('type "test1.txt" ^& del "test1.txt"') do (
    set /a "line_counter+=1"
    set "line=%%B"
    if !line_counter! EQU %line_number% (
        (echo %line_number%:%replace%)>>"test1.txt"
    ) else (
        (echo !line!)>>"test1.txt"
    )
)

Если ваш фактический test1.txt файл не содержит n:, замените (echo %line_number%:%replace%)>>"test1.txt" на (echo %replace%)>>"test1.txt" в во всех случаях .

Если то же самое происходит с файлом test2.txt, замените for /F "tokens=2 delims=:" %%A IN (test2.txt) do ( на for /F "delims=" %%A IN (test2.txt) do ( в во всех случаях .

И, предполагая, что значение, хранящееся в переменной replace, находится в третьей строке, используйте:

@echo off
setlocal EnableDelayedExpansion

set "line_counter=0"
set "line_number=3"

for /F "tokens=2 delims=:" %%A IN (test2.txt) do (
    set /a "line_counter+=1"
    if !line_counter! EQU 3 (
        set "replace=%%A"
    )
)

set "line_counter=0"

for /F "delims=" %%B IN ('type "test1.txt" ^& del "test1.txt"') do (
    set /a "line_counter+=1"
    set "line=%%B"
    if !line_counter! EQU %line_number% (
        (echo %line_number%:%replace%)>>"test1.txt"
    ) else (
        (echo !line!)>>"test1.txt"
    )
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...