Вот более общая версия, которая позволяет файлу замены (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
).