Windows Batch – Как создать массив с уникальным набором значений?

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

@echo off
setlocal enableDelayedExpansion
set i=0
for %%f in (*00.DIF) do (
  set fname=%%~nf
  set year=!fname:~-4!
  ::echo !year!
  set Obj[!i!].name=!year!
  set /a i=!i!+1
)
set lastindex=!i!

for /L %%f in (0,1,!lastindex!) do ( 
  echo !Obj[%%f].name!
)

Будем честны. Даже если вы сможете заставить эту работу работать, ее никогда не удастся поддерживать. Линии выглядят как случайный шум, и когда вы вернетесь к ним шесть недель спустя, вы их не узнаете. Как только вы дойдете до этого момента, вам следует использовать язык сценариев: Python, Perl, Ruby, LUA. или даже бить.

Tim Roberts 02.07.2024 20:26
set Obj[ | findstr /e "=!year! && ( echo !year! already exists ) || ( echo unique !year! ). замените команды echo нужными вам функциями. ВНИМАНИЕ: замените :: на REM. :: — это (недопустимая) метка, которая вызывает неожиданное поведение (например, пропуск строк). @Tim: код легко читается. Не существует «лучшего» языка. Часто «лучшее» — это то, к чему вы больше всего привыкли (если оно справляется со своей задачей).
Stephan 02.07.2024 20:38

@TimRoberts — Batch — это язык сценариев. То, что вы не можете этого понять, не означает, что это непонятно (хотя, кстати, забавно, что вы рекомендуете Perl и Ruby). Каждую из этих строк может легко понять человек, обладающий хотя бы поверхностным знанием языка.

SomethingDark 02.07.2024 20:44

(или ... | findstr /elc:"].name=!year!" ..., если .name намекает, что могут быть и другие obj[...].<anythingElse>)

Stephan 02.07.2024 20:51

Я заранее извиняюсь за свое невежество, код, который вы видите выше, создан после дневного поиска в Google. @Стефан, я пытаюсь понять, как применить твой код, и на данный момент мне это удалось, но это не отражает ничего ценного. Он просто повторяет «Obj[0].name=K900» снова и снова. К900 — одно из «годовых» значений.

DKY 02.07.2024 21:25

Очень странно, что все ваши имена файлов заканчиваются на *2000.DIF, *1900.DIF, *1800.DIF, *1700.DIF и т. д. Подождите, я только что увидел ваш комментарий выше; в твоих годах есть символы алфавита!

Compo 02.07.2024 21:35
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
0
6
82
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий
  1. Как уже отмечалось, внутри оператора блока (a parenthesised series of statements) следует использовать операторы REM, а не форму примечания с разорванной меткой (:: comment), поскольку метки завершают блоки, сбивая с толку cmd. :: — это неработающая метка, потому что до :: нельзя добраться с помощью goto, но тем не менее это метка.

  2. Используйте set "var=value" для установки строковых значений — это позволяет избежать проблем, вызванных конечными пробелами. Не назначайте ", обратную косую черту или пробел. Создавайте пути из элементов — как ни странно, это, вероятно, облегчит процесс. Используйте set /a var=value, чтобы установить числовые значения.

  3. Я предпочитаю избегать ADFNPSTXZ (в любом случае) в качестве метапеременных (переменных управления циклом). ADFNPSTXZ также являются модификаторами метапеременных, что может привести к трудно обнаруживаемым ошибкам. (См. for/f в приглашении к документации)

  4. Синтаксис !var! требуется только для доступа к измененному значению var, где var варьируется внутри блока кода (последовательность команд в скобках).

Следовательно, переписанный код:

@ECHO OFF
setlocal enableDelayedExpansion
set /A i=0
SET "chosen = "
REM for %%e in (*00.DIF) do (
for %%e in (1234 0011 4567 7890 0011 4183) do (
  set fname=%%~ne
  set year=!fname:~-4!
  REM echo !year!
  set Obj[!i!].name=!year!
  set /a i+=1
  SET "unaltered=Y"
  FOR %%y IN (!chosen!) DO IF DEFINED unaltered IF "%%y"= = "!year!" (
  SET "unaltered = "
   set /a i-=1
   set "Obj[!i!].name = "
  )
  IF DEFINED unaltered SET "chosen=!year! !chosen!"
)
set /a lastindex=i-1

for /L %%e in (0,1,%lastindex%) do ( 
  echo !Obj[%%e].name!
)
GOTO :EOF

Новая переменная chosen не имеет значения.

Я заменил ваш первый цикл for циклом, который просто обрабатывает серию чисел в целях тестирования. Я также изменил metavariable с f на e.

Используйте более современный синтаксис для увеличения i. см. set/? из подсказки или бесконечные элементы SO для docco.

Новый цикл %%y для поиска chosen ранее присвоенных значений. Обратите внимание на использование unaltered в качестве логического значения: оно либо определено как Y, либо не определено. Если он определен, просто отмените приращение i и удалите назначенное строковое значение «массив». Использование логического значения

Затем, если значение в year новое, unaltered определяется и новое значение year добавляется к списку значений в chosen.

После обработки всего списка lastindex устанавливается в i - 1, поскольку i указывает на следующий элемент, который необходимо заполнить.

--- редактировать -----

Перекаффиенировав, воспользовавшись идеей Стефана findstr...

@ECHO OFF
setlocal enableDelayedExpansion
set /A i=0
SET "chosen = "
REM for %%e in (*00.DIF) do (
for %%e in (1234 0011 4183 4567 7890 0011 7890 7890 7890 4183) do (
  set fname=%%~ne
  set year=!fname:~-4!
  REM echo !year!
  ECHO !year!|FINDSTR ": !chosen!"  >NUL
  IF ERRORLEVEL 1 (
   set Obj[!i!].name=!year!
   set /a i+=1
   SET "chosen=!year! !chosen!"
  )
)
set /a lastindex=i-1

for /L %%e in (0,1,%lastindex%) do ( 
  echo !Obj[%%e].name!
)
GOTO :EOF

Здесь, если year нет в строке chosen, то findstr установит errorlevel в 1; если да, то errorlevel будет установлено на 0.

findstr по умолчанию находит любую строку, разделенную пробелом, но ему не нравятся пустые строки, поэтому в список добавляется двоеточие как пустышка; двоеточие в имени файла невозможно.

Только если yesr отсутствует в списке, он будет добавлен.

Нам не нужен фактический вывод findstr, просто он устанавливает errorlevel, поэтому вывод >nul отправляется в никуда.

Это похоже на сон! Спасибо! Что именно делает (1234 0011 4567 7890 0011 4183)? Также спасибо за подробности. Я буду перечитывать это снова и снова, пока это не обретет смысл, лол!

DKY 02.07.2024 22:03

Эта строка просто устанавливает %%e в эти строки последовательно в целях тестирования, чтобы мне не приходилось создавать *00.dif файлы для тестирования процедуры. Просто удалите эту строку и снимите REMark с (измененного) оригинала, чтобы вместо этого обрабатывать имена файлов.

Magoo 02.07.2024 22:30

Вы не указали, важен ли порядок элементов массива. Если нет, то есть очень простой способ решить вашу проблему: вместо числового индекса используйте в качестве индекса значение!

@echo off
setlocal enableDelayedExpansion
for %%f in (*00.DIF) do (
  set fname=%%~nf
  set year=!fname:~-4!
  REM echo !year!
  set Obj[!year!].name=!year!
)

REM Show array elements
for /F %%a in ('set Obj[') do ( 
  echo %%a
)

Поскольку не может быть более одного элемента с одним и тем же индексом, не имеет значения, определен ли один и тот же элемент более одного раза. В конце концов, существует только один элемент с таким индексом.

PS. Почему вы назвали Obj[#].name массив? Это очень необычно, потому что часть «имя» не отображается в значениях. ИМХО, имя могло бы быть и получше Year[#]

Другие вопросы по теме