WedX - журнал о программировании и компьютерных науках

Пакетная замена и добавление 1 к числу между определенным текстом в XML-файле

Хорошо, я пытаюсь написать пакет, чтобы «захватить» серийный номер для пользователя. Серийный номер поступает из другой системы, и нам приходится периодически вводить серийный номер вручную. Я хотел бы, чтобы пользователь запустил пакетный файл, который добавит 1 и заменит старый номер, а затем выведет «Ваш серийный номер ##», где ## - новый номер. Я почти уверен, что смогу справиться с эхо-частью и записью во временный файл и перезаписью старого файла, но это средняя часть, с которой я борюсь. XML-файл, который редактируется, это:

<?xml version="1.0"?>
<BatchSettings xmlns:xsi="https://w3.org/2001/XMLSchema-instance" xmlns:xsd="https://w3.org/2001/XMLSchema">
  <SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
  <LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
  <LastSerialNumber>10</LastSerialNumber>
</BatchSettings>

номер, который мне нужно изменить, - это ## между "lastserialnumber" <LastSerialNumber>10</LastSerialNumber>. Я пробовал несколько разных подходов, и этот первый близок, но определенно не сигарный.

@echo off
set /a "replace=10"
set /a "replaced=replace+1"

set "source=Test.txt"
set "target=Test2.txt"

setlocal enableDelayedExpansion
(
   for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %source%') do (
      set "line=%%b"
      if defined line set "line=!line:%replace%=%replaced%!"
      echo(!line!
   )
) > %target%
endlocal

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

я тоже пробовал

@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"

@echo off 
    setlocal enableextensions disabledelayedexpansion 

    (for /f "delims=" %%a in (%SrcFile%
    ) do for /f "tokens=2 delims=1234567890 " %%b in ("%%~a"
    ) do if not "%%b"==":"  (
        echo(%%~a
    ) else for /f "tokens=1 delims=  <LastSerialNumber>:" %%c in ("%%~a"
    ) do (
        set /a "n=%%c+1"
        setlocal enabledelayedexpansion
        echo(<LastSerialNumber>!n!:
        endlocal
    )) > %OutFile%

но это не увеличивает число и обрезает оставшийся текст. а этот просто хлам, который вообще не работает.

@Echo Off
Set "SrcFile=Test.txt"
Set "OutFile=Test2.txt"

setlocal EnableDelayedExpansion

(for /F "delims=" %%a in ('findstr /I /L "<LastSerialNumber>" %SrcFile%') do (
   set "line=%%a
   set "line=!line:*<LastSerialNumber>=!"
   for /F "delims=<" %%b in ("!line!") do echo %%b+1
)) > %oldnumber%

set "newnumber=%oldnumber%+1"

(
   for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %SrcFile%') do (
      set "line=%%b"
      if defined line set "line=!line:%oldnumber%=%newnumber%!"
        echo(!line!
   )
) > %OutFile%
endlocal

  • Для манипулирования данными XML я рекомендую использовать язык, который изначально его поддерживает, например powershell или javascript, например; пакетный файл обрабатывает его как обычный текст, поэтому он подвержен его повреждению. В любом случае, всегда ли <LastSerialNumber>10</LastSerialNumber> встречается, как показано, в одной отдельной строке? 28.05.2020
  • Строка lastserialnumber всегда выглядит так, как показано, просто очевидно, что разные серийные номера. 29.05.2020

Ответы:


1

Сначала давайте кратко рассмотрим ваши попытки:

  1. терпит неудачу, так как ищет только 10 и вообще не учитывает теги <LastSerialNumber>.
  2. терпит неудачу, потому что вы пытаетесь определить строку для опции delims= для for /F, хотя на самом деле она определяет набор символов.
  3. терпит неудачу, потому что вы пытаетесь перенаправить (>) в переменную, которая не может работать, вы можете перенаправить только в файлы. Кроме того, вы пытаетесь выполнить арифметику (+1), но это работает только при использовании set /A. И последний раздел имеет ту же проблему, что и подход 1.

Вот как вы можете это сделать:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=%~1" & rem // (input file; `%~1` is the first argument)
set "_TAG=<LastSerialNumber>" & rem // (opening tag)

rem // Loop over all (non-empty) lines of the input file:
for /F "usebackq delims=" %%L in ("%_FILE%") do (
    rem // Store current line string:
    set "LINE=%%L"
    rem // Toggle delayed expansion to avoid troubles with `!`:
    setlocal EnableDelayedExpansion
    rem // Split off opening tag from line string:
    set "TEST=!LINE:*%_TAG%=!"
    rem // Check whether opening tag has been found:
    if not "!TEST!"=="!LINE!" (
        rem // Opening tag found, hence split off closing tag:
        for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
            rem // Get extracted number and increment it:
            set /A "NUM=%%S+1"
            rem // Return rebuild line with incremented number:
            echo(  !_TAG!!NUM!^<%%T
        )
    ) else (
        rem // Opening tag not found, hence return original line:
        echo(!LINE!
    )
    endlocal
)

endlocal
exit /B

Учитывая, что пакетный файл называется inc-serial.bat, вызовите его с входным файлом XML в качестве аргумента командной строки (скажем, test.xml в текущем каталоге):

inc-serial.bat "test.xml"

Чтобы записать вывод в другой файл, используйте перенаправление (>):

inc-serial.bat "test.xml" > "test_NEW.xml"
28.05.2020
  • Это прекрасно работает, и я могу вызвать его с другим пакетным файлом, который работает, но как мне отредактировать этот код, чтобы поместить его в пакетный файл. Я бы предпочел не иметь 2 отдельных пакетных файла, забивающих каталог, и помнить, перемещаю ли я их когда-либо, если мне это не нужно. Я пытался заменить %~1 исходным именем файла и добавить переменную выходного файла или напрямую перенаправить (например, › Test2.xml), но независимо от того, где я его разместил, он не работает. 29.05.2020
  • Да, замените %~1 на путь к входному файлу (если вы просто ставите чистое имя файла, оно относится к текущему рабочему каталогу; если вы ставите префикс %~dp0, оно относится к родительскому каталогу скрипта). Чтобы определить выходной файл (например, set "_OUTF=..." таким же образом), заключите весь блок цикла for /F в другую пару круглых скобок и добавьте > "%_OUTF%" в конце )... 29.05.2020

  • 2

    Хорошо, наконец-то я получил код, который работает так, как мне нужно. Просто нужно установить каталоги, которые я оставил здесь по понятным причинам. Большое спасибо aschipfl за помощь. Этот код индексирует серийный номер на единицу, записывает во временный файл, затем выполняет обратную запись и удаляет временный файл. Затем отображает новый серийный номер.

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    
    rem // Define constants here:
    set "_FILE=BatchTest.xml" & rem // (input file; `%~1` is the first argument)
    set "_TAG=<LastSerialNumber>" & rem // (opening tag)
    
    rem // Loop over all (non-empty) lines of the input file:
    (for /F "usebackq delims=" %%L in ("%_FILE%") do (
        rem // Store current line string:
        set "LINE=%%L"
        rem // Toggle delayed expansion to avoid troubles with `!`:
        setlocal EnableDelayedExpansion
        rem // Split off opening tag from line string:
        set "TEST=!LINE:*%_TAG%=!"
        rem // Check whether opening tag has been found:
        if not "!TEST!"=="!LINE!" (
            rem // Opening tag found, hence split off closing tag:
            for /F "tokens=1* eol=< delims=< " %%S in ("!TEST!") do (
                rem // Get extracted number and increment it:
                set /A "NUM=%%S+1"
            rem // Return rebuild line with incremented number:
                echo(  !_TAG!!NUM!^<%%T
            )
        ) else (
            rem // Opening tag not found, hence return original line:
            echo(!LINE!
        )
        endlocal
    ))>BatchTestTmp.xml 
    
    
    copy /v /y "BatchTestTmp.xml" "BatchTest.xml" & cls
    
    del "BatchTestTmp.xml"
    
    set "LINE=4"
    (for /f "usebackq delims=" %%a in (`more +%LINE% BatchTest.xml`) DO (
    set result=%%a
    setlocal EnableDelayedExpansion
    echo Your Serial Number is !result:~20,2!
    pause 
    goto :leave
    ))
    :leave
    endlocal
    
    exit /B
    
    29.05.2020

    3

    Впечатляет то, что старый добрый Batch все еще может решить эту проблему. Очевидно, что он не предназначен для поддержки XML, поэтому я бы порекомендовал инструмент, который изначально поддерживает его с помощью выражений XPath/XQuery, например xidel.

    Часть ECHO проста:

    xidel -s input.xml -e "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1,' is the new number')"
    Your serial number is 10 where 11 is the new number
    

    Редактировать немного сложнее:

    xidel -s input.xml --xquery "serialize(transform(/,function($x){$x/(if (name()='LastSerialNumber') then <LastSerialNumber>{node() + 1}</LastSerialNumber> else .)}),{'omit-xml-declaration':false()})"
    <?xml version="1.0" encoding="UTF-8"?>
    <BatchSettings>
      <SerialFormat>{0:yyMMdd}{1:00}</SerialFormat>
      <LastSerialDate>2020-05-27T00:00:00-05:00</LastSerialDate>
      <LastSerialNumber>11</LastSerialNumber>
    </BatchSettings>
    

    - transform() перебирает каждый элемент-узел. Он оставляет каждый из них нетронутым, за исключением случаев, когда он находит <LastSerialNumber>. Затем он создает новый с исходным значением + 1.
    - serialize() упорядочивает вывод, а с помощью {'omit-xml-declaration':false()} добавляет сверху объявление xml.

    serialize() просто выводит вывод (на стандартный вывод). Чтобы записать вывод в файл, используйте file:write(). вместо:

    xidel -s input.xml --xquery "file:write('input.xml',transform(/,function($x){$x/(if (name()='LastSerialNumber') then <LastSerialNumber>{node() + 1}</LastSerialNumber> else .)}),{'omit-xml-declaration':false()})"
    

    Чтобы объединить это с частью ECHO:

    xidel -s input.xml --xquery "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1,' is the new number'),file:write('input.xml',transform(/,function($x){$x/(if (name()='LastSerialNumber') then <LastSerialNumber>{node() + 1}</LastSerialNumber> else .)}),{'omit-xml-declaration':false()})"
    

    Предварительный запрос (с необходимыми escape-символами):

    xidel -s input.xml --xquery ^"^
      concat(^
        'Your serial number is ',^
        //LastSerialNumber,^
        ' where ',^
        //LastSerialNumber + 1,^
        ' is the new number'^
      ),^
      file:write(^
        'input.xml',^
        transform(^
          /,^
          function($x){^
            $x/(^
              if (name()='LastSerialNumber') then^
                ^<LastSerialNumber^>{node() + 1}^</LastSerialNumber^>^
              else^
                .^
            )^
          }^
        ),^
        {'omit-xml-declaration':false()}^
      )^
    "
    

    Это напечатает Your serial number is 10 where 11 is the new number и редактирует (и перезаписывает) 'input.xml'.

    [edit]
    {'omit-xml-declaration':false()} работает только с Xidel 0.9.9 (начиная с xidel-0.9.9.20190104.6739.b64562007cb7). В Xidel 0.9.8 вы можете вручную добавить xml-декларацию:

    xidel -s input.xml --xquery "concat('Your serial number is ',//LastSerialNumber,' where ',//LastSerialNumber + 1,' is the new number'),file:write('input.xml',('<?xml version=\"1.0\" encoding=\"UTF-8\"?>',transform(/,function($x){$x/(if (name()='LastSerialNumber') then <LastSerialNumber>{node() + 1}</LastSerialNumber> else .)})))"
    

    [/править]

    26.07.2020
    Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: wedx@cp9.ru