0

My problem: I have a .txt file of strings I'd like to sort through and label in the range 1 through 4. I only want to keep items 1 and 2 while deleting 3 and 4. I know I can use %4 to keep the 0 and 1 tokenized items while deleting 2 and 3, but the trouble is using an if statement while modulating, and doing that over the whole file. The file size will be at least 5000 /n lines long.

Here's the code I've had the most success with so far. I'm using the code I found in the top comment here as a base.

@echo on
for /f "tokens=*" %%a in ('findstr /n .* "C:\...\TEXTFILE.txt"') do (
    set "FullLine=%%a"
    for /f "tokens=1* delims=:" %%b in ("%%a") do (
        setlocal enabledelayedexpansion
        set "LineData=!FullLine:*:=!"
        set /a MODVAR = %%b%%2
        if %%b == !MODVAR! >"TEXTFILE.txt" echo(!LineData! 
        endlocal
    )
)
pause

This replaces only the text from line 1. I know I said I want line 1 and 2, but I imagine I'd use an else statement to get that second line as well. Just need the current code to work before I try that problem.

I've read that it would be easier to use a regex find and replace, but I'd much prefer to find a solution using batch so I can write (get help with) the current code and be done. Rather than copying/pasting possibly multiple times.

Edit: an example of my initial text document is:

    aaa
    bbb
    ccc
    ddd
    eee

(and so on) where i want to only keep aaa, bbb, and eee

egrave
  • 11
  • 3
  • 1
    Well, I would prefer seeing an actual use case, in other words show the example of the input and demonstrate the expected output results. – Gerhard Apr 26 '21 at 07:14
  • 2
    It's unclear how your input textfile looks like, please show an example. The code you copied is just for selecting a single line by its line number. But the part `set /a ccc=%%b%%2` looks odd. Btw. You placed the `endlocal` at the wrong place – jeb Apr 26 '21 at 07:16
  • 2
    @jeb, yeah, I agree, that is a one strange way to set a value to be 1. – Gerhard Apr 26 '21 at 07:18
  • @Gerhard I editted the endlocal, as well as the set /a var to keep from confusion in the example. my reason for doing that is because i was trying to simplify using modulo in the if statement since I couldn't find another workaround – egrave Apr 26 '21 at 08:42
  • still not sure, I understand you. Is it "take two lines, ignore two, take two ignore two ... etc."? – Stephan Apr 26 '21 at 09:13
  • @Stephan yes that's the pattern – egrave Apr 26 '21 at 10:40

3 Answers3

1

Modulo is great if you want "each n-th line". Especially for "take two, skip two", there is a better way (a logical bitwise AND).

@echo off
setlocal enabledelayedexpansion

(for /f "tokens=1* delims=:" %%i in ('findstr /n "^" in.txt') do (
  set /a "modvar=(%%i-1) & 2"
  REM echo %%i, !modvar!,   %%j >con
  if !modvar! == 0 echo(%%j
))>out.txt

I also put the whole output to the outfile in one go. That's much faster than opening the file for each line, writing it and closing the file again.

Remove the REM for troubleshooting. It shows you what exactly happens with modvar.

(Note: this method works for "take N, skip N" only when N is a power of two (1,2,4,8,16,...))

Just for completeness, here is how to do it with MODULO:

@echo off
setlocal enabledelayedexpansion

(for /f "tokens=1* delims=:" %%i in ('findstr /n "^" in.txt') do (
  set /a "modvar=(%%i-1) %% 4"
  REM echo %%i, !modvar!,   %%j >con
  if !modvar! lss 2 echo(%%j
))>out.txt
Stephan
  • 53,940
  • 10
  • 58
  • 91
1

I'm not entirely sure from your description of your actual task, so this is my reading of it, your intention is to recreate the lines of a file as another but with lines 3 and 4 omitted.

Here's one possibility, subject to whether tabs must be preserved:

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
<"input.txt" (
    Set /P "#1="
    Set /P "#2="
    SetLocal EnableDelayedExpansion
    Set /P "=!#1!"<NUL & Echo(
    Set /P "=!#2!"<NUL & Echo(
    EndLocal
    %SystemRoot%\System32\more.com +4
) >"output.txt"
Compo
  • 36,585
  • 5
  • 27
  • 39
0

I am focusing on the example you gave of line 3 and 4 only.

On the single file as per your example:

@echo off
setlocal enabledelayedexpansion
set "file=C:\...\TEXTFILE.txt"
for /f "tokens=*" %%f in ('type "!file!" ^| find /v /n "" ^& break ^> "!file!"') do (
         set "line=%%f"
         if "!line:~0,3!" == "[3]" set "line="
         if "!line:~0,3!" == "[4]" set "line="
         if defined line set "line=!line:*]=!"
         if defined line echo(!line!)>>"!file!"
   )

Or to loop through all .txt files and perform a delete of lines 3 and 4

@echo off
setlocal enabledelayedexpansion
for %%i in (*.txt) do (
    set "file=%%~i"
    for /f "tokens=*" %%f in ('type "!file!" ^| find /v /n "" ^& break ^> "!file!"') do (
         set "line=%%f"
         if "!line:~0,3!" == "[3]" set "line="
         if "!line:~0,3!" == "[4]" set "line="
         if defined line set "line=!line:*]=!"
         if defined line echo(!line!)>>"!file!"
   )
)

To understand what happens: We type through the file, add line numbers by using find /v /n in brackets. i.e each line will append beginning of line [1],[2] etc.

Then we match to see if the first 3 characters matches either [3] or [4] if they do, clear the variable. Then we simply test each time if the variable is defined, if not (in the case for line 3 and 4) we simply skip echoing those lines.

So given your examples of:

aaa
bbb
ccc
ddd
eee

I type the file before, to see the content, run the batch-file then type the file again to see the results:

enter image description here

Gerhard
  • 22,678
  • 7
  • 27
  • 43
  • this works but as stephan put it a better way to phrase the pattern of which lines to print is "print 2 lines skip two lines". That was where I was getting the idea of using a modulo to simplify which lines to print or skip over any x amount of lines – egrave Apr 26 '21 at 10:43