Войти

Показать полную графическую версию : Как из for получить значение из одной строки смещаясь на 1 при каждом проходе


Voodooman
27-08-2011, 14:57
Условия:
Имею 2 "рафинированных" файла со списками, в первом файле по имени name.txt в каждой строчке имеются строки с названиями, во втором файле имеются айди соответсвующие именам именам в первом файле, то есть айди из первой строки соответствует имени из первой строки, айди из 555 строки id.txt соответствует имени из 555 строки в id.
Требуется:
Получить в каждом проходе 2 соответствующие друг друг переменных из одной и той же строчки в двух разных файлах, при следующем проходе перезаписать их новыми переменными со следующей строки

Код:
setlocal enabledelayedexpansion
ECHO on
SET N=0
FOR /F "usebackq tokens=* delims=" %%A IN (name.txt) DO (
FOR /F "usebackq skip=%N% tokens=* delims=" %%B IN (id.txt) DO (
SET /a N=!N!+1
SET GameName=%%A
SET GameId=%%B
)
ECHO !GameName!=!GameId!
)
pause

Проблема:
Первый For получающий имя без всяких проблем каждый цикл переходит к новой строчке пока не достигнет конца файла, с каждым новым проходом я получаю нужно мне имя файла.
Но второй цикл For, заключенный внутри первого, при выходе из цикла и возврате в предыдущий из которого уже выводится эхо, всегда дает мне айди из последней строки таким образом разным именам всегда присваивается 1 и тот же айди.
Я пробовал перенести эхо внутрь второго цикла, но при этом получался противоположный эффект - я получал 1 и то же имя с разными айди.
Я попробовал добавить skip=N и ввел простой счетчик прохода N=N+1 и пытаюсь его значение подставить в качестве значения пропускаемых строк, по логике я на самом деле должен получить такой результат как и до этого (это я уже сообразил после добавления, ведь даже если я начиная с другой строки, все равно цикл закончится последней :D ), но по странной причине ошибка в синтаксисе.
Ну в общем не знаю, как заставить внутренний цикл читать одну заданную номерную строчку вместо всех и завершаться. Я где то видел что внутри IN указывали примерно вот таким способом ([1] [555]) номер нужной строчки, но как это совместить с именем файла и каков правильный синтаксис еще и с подстановкой переменной N?

П.С. - Как я понял find и findstr в моем случае не получится использовать, так как изначально неизвестно что искать, кроме номера строчки. В общем очень нуждаюсь в помощи, залип с такой простой вещью, а документация что в винде что в нете слишком куцая чтобы найти решение.

Iska
27-08-2011, 15:52
Voodooman, для правильного понимания не хватает примеров всех четырёх файлов: что есть и что нужно получить в итоге.

Voodooman
27-08-2011, 16:02
4 файла???
2 же))
Я вроде бы доступно все объяснил, не хочу показаться грубым, но чуть выше все разжевано на пальцах так, что можно обойтись и без примеров и все правильно понять.
Но ладно, попробую еще более доступно объяснить:

name.txt

name 1
name 2
another name
one more name
etc


id.txt
125162
446
68568
434637
3535

Что я получаю:
name 1=3535
name 2=3535
another name=3535
one more name=3535
etc=3535

Что нужно получить:
name 1=125162
name 2=446
another name=68568
one more name=434637
etc=3535

Foreigner
27-08-2011, 16:56
Voodooman,

Что нужно получить:
Код:

name 1=125162
name 2=446
another name=68568
one more name=434637
etc=3535

При условии, что все файлы без пустых строк

@echo off
setlocal

<id.txt set /p "firstid="
<names.txt set /p "firstname="

echo %firstname%=%firstid%
set "skip=0"
for /f %%i in (id.txt) do set /a cnt+=1

:loop
if %skip% equ %cnt% goto:eof
set /a skip+=1

for /f "skip=%skip% tokens=*" %%i in (id.txt) do (

for /f "skip=%skip% tokens=*" %%j in (names.txt) do echo %%j=%%i && goto:loop

)

Voodooman
27-08-2011, 18:13
Я уже справился с задачей сам - решил совсем не использовать дампы со списком имен и айди, так как не нашел нормального способа заставить внутренний цикл for обработать одну строчку и завершиться, вместо прохода по всем строчкам, по этому использовал чтение одного ini файла соответсвующего имени напрямую, так хоть во внутреннем for не появляется десяток лишних циклов:

setlocal enabledelayedexpansion
echo off
call :MainFunction
pause
exit

rem предпочитаю делить код на легко читаемые и понимаемые подпрограммы\функции и вызывать при необходимости
:MainFunction
FOR /F "tokens=* delims=" %%A IN ('dir *.ini /b /o:gn') DO (
FOR /F "usebackq tokens=3* delims=/" %%B IN ("%%~nA.ini") DO (
SET GameName=%%~nA
SET GameId=%%B
) && SET /a N=!N!+1
call :Debug
)
exit /b


:Debug
echo !N!=!GameName!=!GameId!
exit /b


Но меня все еще интересует как сделать первым методом, должен же быть способ выборочного чтения конкретной строчки.
Foreigner, cкрипт работает, но что это за синтаксис такой страшный?
Можно пояснения как он работает и в чем моя ошибка?

Что делает вот это и зачем оно вообще нужно?
<id.txt set /p "firstid="

Зачем выводить первое эхо, ставить две переменных счетка skip и cnt и еще и сравнивать их? Зачем выпрыгивать из цикла for во внешний loop, неужели нет более простого и лаконичного способа прочитать 1 конкретную строку из for и закончить цикл?
Можно в более удобоваримом виде и с комментариями?

Iska
27-08-2011, 18:40
4 файла??? 2 же))

Я вроде бы доступно все объяснил, не хочу показаться грубым, но чуть выше все разжевано на пальцах так, что можно обойтись и без примеров и все правильно понять. »
Вот вам и ответ: я воспринял текст не верно, как два исходных и два — результирующих. А оказалось — три файла. Именно их содержание Вы привели далее.

Видите ли, коллега, приведение примеров позволяет дать наглядный образец и помогает лучше воспринимать формальный текст поставленного задания.

P.S. Можно спросить, например, у Foreigner'а — облегчили ли ему приведённые примеры понимание сути вопроса, или же они лишние?

Foreigner, cкрипт работает, но что это за синтаксис такой страшный? »
Нормальный синтаксис ;). Я не проверял работоспособность, но выглядит всё вполне цивильно и наглядно.

Что делает вот это и зачем оно вообще нужно?
<id.txt set /p "firstid=" »
Первая строка файла «id.txt» считывается в переменную окружения «firstid».

Но меня все еще интересует как сделать первым методом, должен же быть способ выборочного чтения конкретной строчки. »
неужели нет более простого и лаконичного способа прочитать 1 конкретную строку из for и закончить цикл? »
В принципе — есть. «more» умеет выводить текст, начиная с указанной строки. Другое дело, что ограничить вывод последующих строк через «more» не представляется возможным, и придётся всё одно отсекать вывод этих последующих строк, например, разбором через FOR и прерыванием цикла разбора на первом шаге. Например, читаем четвёртую строку файла «some.txt»:
@echo off
setlocal enableextensions enabledelayedexpansion

for /f "delims=" %%i in ('^<"some.txt" more +3') do set sValue=%%i & goto :lBreak
:lBreak

echo %sValue%

endlocal
exit /b 0

Другой возможный способ прерывания цикла описал amel27 (http://forum.oszone.net/member.php?userid=38813) здесь (http://forum.oszone.net/post-1614493.html#post1614493).

Foreigner
27-08-2011, 19:00
Что делает вот это и зачем оно вообще нужно?
<id.txt set /p "firstid="
Берет первую строчку из файла. что бы потом можно было ее пропустить ( skip ). Т.к. skip=0 выдаст ошибку.
Зачем выпрыгивать из цикла for во внешний loop, неужели нет более простого и лаконичного способа прочитать 1 конкретную строку из for и закончить цикл?
Здесь же два цикла, по одному для каждого файла. Причем они должны быть одновременными. Я не придумал решения проще, чем прерывать оба цикла после прочтения первых строк в файлах и увеличения кол-ва пропущенных строк при каждой итерации в :loop. А переменные сравниваются для того, что бы не уйти в бесконечный цикл. Можно конечно все загнать в коллекцию переменных, а потом их разименовать:

@echo off
setlocal enabledelayedexpansion

for /f "tokens=*" %%i in (names.txt) do call:set names "%%i"
set "cnt="
for /f "tokens=*" %%i in (id.txt) do call:set id "%%i"

for /l %%i in (1,1,%cnt%) do echo !names_%%i!=!id_%%i!
goto:eof

:set
set /a cnt+=1
set "%1_%cnt%=%~2"

Просто я по возможности избегаю enabledelayedexpansion при echo-выводе текста.

Iska
27-08-2011, 19:06
Можно конечно все загнать в коллекцию переменных, а потом их разименовать: »
Кстати, тоже вариант. И, главное — самый быстрый. Лишь бы в ограничения командного процессора на целочисленные переменные число строк уместилось.
Просто я по возможности избегаю enabledelayedexpansion при echo-выводе текста. »
А почему? Я, напротив — всегда использую.

Foreigner
27-08-2011, 19:13
А почему? Я, напротив — всегда использую. »
Да что-то как-то так пошло, чистые батники и так с текстом работают "не слишком", а тут еще придется заботится о "!". Правда это не отменяет "%". Есть конечно универсальные решения, но это усложняет код. Имхо

Voodooman
27-08-2011, 19:42
Ну я тут на самом деле эхой из дебаг функции заменил кучу других функций и значительно скрипт сократил, чтобы не отвлекались на ненужные вещи, в данном случае меня интересуе соответсвие строк двух переменных.

Вот сейчас в своем решении без дампа списка файлово решил добавить по мимо считывания имени из названия ини файла и считывания айди из самого инишника, еще и считывания параметра с именем иконки, но вот беда все та же случилась - for начинает парсить все строки непрерывно и в итоге выдает мне последнюю по выходу из его цикла.
Я бы мог в 3 форе как и во 2 использовать делиметер с бекслешем и токеном, но вот беда еще в том, что количество бекслешей в искомой строке может различаться в некоорых инишника и соответсвенно я не знаю точно сколько токенов отсчитывать от начала строки, может есть способ отсчитывать токены от конца или число симоволов из нужной строчки от конца отсчитать и взять за переменную?

Дело в том что ваши методы джама из for во вне мне не подходят, так как у меня 3 фложенных for и прыжок прервет все 3, я попробовал сделать функцию внутри for b выпрыгнуть лишь за пределы 3го во второй, но увы обломался, похоже cmd не поддерживает таких джампов внутри цикла из более глубокого в менее без прерывания последнего.

В общем мне все еще надо считать одну строку из файла и прервать наследующий for не прерывая родительский или заменить какой то командой которая однозначно умеет читать только одну строку.
Я поверить не могу что МС выдумывая кучу сложных вещей для бат файла не придумали простого способа задать и исполнить лишь одну строку.


Вот как можно прервать один фор после первого или заданного прохода не прерывая второго? Может можно использовать как то skip относительно конца файла? например так skip=3,-3 - не читать первые и последние 3 строчки, а если между ними всего одна, то как раз получается нужный результат.

Iska
27-08-2011, 20:53
Да что-то как-то так пошло, чистые батники и так с текстом работают "не слишком", а тут еще придется заботится о "!". Правда это не отменяет "%". Есть конечно универсальные решения, но это усложняет код. Имхо »
Foreigner, спасибо, ясно.

Дело в том что ваши методы джама из for во вне мне не подходят, так как у меня 3 фложенных for »
Voodooman, а может тогда стоит перейти на WSH?

Voodooman
27-08-2011, 21:33
Не, WSH не стоит, я с ним не знаком и тут мне уже принципиально надо заставить бат файл каким то хитрым способом прочить только одну строчку!)) Я не успокоюсь пока не заставлю его это сделать)))

Решил поискать внешние консольные утилиты для упрощения задачи и поставки их в комплекте вместе с готовым батником, наткнулся на эту утилиту для чтения, изменения и удаления параметров из ини файлов через батники http://www.horstmuc.de/wbat32.htm#inifile

Сейчас попытаюсь еще найти более широкопрофильную утилиту, которая может читать заданную строчку в любую сторону, помню мне однажды попадалась такой монстр, который через командную строку умел делать все, найти бы его сейчас и не извращаться со встроенным ограниченным функционалом)

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

Кстати, я забыл, как мне из полученной строки взять и использовать в качестве переменной последние 44 символа без всяких делиметеров и токенов? какие там операторы нужно использовать?

kiripanda
27-08-2011, 21:46
Просто я по возможности избегаю enabledelayedexpansion при echo-выводе текста. »
for /l %%j in (1,1,%cnt%) do call echo %%names_%%j%%=%%id_%%j%%

Foreigner
27-08-2011, 22:04
kiripanda, можно и так.

Voodooman
27-08-2011, 22:25
А кто нибудь на мои вопросы здесь ответит?))
еще раз спрошу - как взять значение из указанной строчки и прервать лишь 1 фор после этого чтобы избежать парсинга отсальных строк?

Iska
27-08-2011, 23:01
Не, WSH не стоит, »
Это как?

«%systemroot%\system32\wscript.exe» наличествует?
я с ним не знаком и тут мне уже принципиально надо заставить бат файл каким то хитрым способом прочить только одну строчку!)) Я не успокоюсь пока не заставлю его это сделать))) »
Спасибо, Ваша цель понятна.

А кто нибудь на мои вопросы здесь ответит?)) »
Я даже не понял вопрос:
как взять значение из указанной строчки и прервать лишь 1 фор после этого чтобы избежать парсинга отсальных строк? »

Voodooman
27-08-2011, 23:34
(флейм удален)

Iska
28-08-2011, 01:29
давай ты не будешь играть в дурака?
…»
Предпочитаю лучше выглядеть дураком и спросить, нежели изображать понимание там, где его у меня нет. Если Вы об этом.

По поводу Ваших прочих многих слов, не относящихся к сути вопроса, равно тона, коим они высказаны… Не получается у Вас сарказм.

Что ж, продолжайте дискуссию в том же духе — и обрящите. Я же — учту на будущее.

Foreigner
28-08-2011, 05:54
Voodooman,
Штатными средствами, без счетчиков и не зная содержимое строки, этого сделать нельзя. Для парсинга текста написано много утилит -- sed, awk, grep, gsar и т.д.




© OSzone.net 2001-2012