Reputation: 3
I have a text file that lists alphanumeric codes such as follows:
17YYUIO
QUICK77
RTY8900
I would like a batch file that either modifies the original text file so everything is double spaced OR simply a result is echoed to the screen as follows:
1 7 Y Y U I O P
Q U I C K 7 7 7
R T Y 8 9 0 0 Q
Basically, I am trying to get Microsoft Narrator to read individual characters from a text file instead of Narrator attempting to pronounce the characters as words.
Any ideas on whether adding spaces between every character in a text file using a batch file is possible?
Upvotes: 0
Views: 4844
Reputation: 130819
I would use my JREPL.BAT regular expression command line text processing utility, as the solution is simpler and faster than any pure batch solution (disregarding the complexity of the utility iteslf)
Write to screen
jrepl "(?!^|$)" " " /f file.txt
Write to new file
jrepl "(?!^|$)" " " /f file.txt /o new.txt
Overwrite original
jrepl "(?!^|$)" " " /f file.txt /o -
But if you want a pure batch solution to replace the original file, then I think this is about as fast as you can get and still support all characters other than null, preserve white space, and support lines of max length ~4090 (~8180 after inserting spaces). The logic is very straight forward, once you add the cryptic code to compute the length of a string. But you can productively use :strlen even if you don't understand how it works.
@echo off
setlocal disableDelayedExpansion
set "file=%~1"
>"%file%.new" (
for /f "delims=" %%A in ('findstr /n "^" "%file%"') do (
set "in=%%A"
setlocal enableDelayedExpansion
set "in=!in:*:=!"
call :strlen in len
set /a len-=1
set "out="
for /l %%N in (0 1 !len!) do set "out=!out! !in:~%%N,1!"
if defined out (echo(!out:~1!) else echo(
endlocal
)
)
move /y "%file%.new" "%file%" >nul
exit /b
:strlen <stringVar> <resultVar>
(
setlocal EnableDelayedExpansion
set "s=!%~1!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~2=%len%"
exit /b
)
Upvotes: 0
Reputation: 67216
@echo off
setlocal EnableDelayedExpansion
(for /F "delims=" %%a in (input.txt) do (
set "output="
for /F "delims=" %%b in ('cmd /U /C echo %%a^| find /V ""') do (
set "output=!output! %%b"
)
echo !output:~1!
)) > output.txt
rem Replace the input file for the result
move output.txt input.txt /Y
This solution makes good use of the fact that /U
switch in cmd.exe
creates Unicode output, that is, each input character is converted into two output bytes where the first byte of each pair is a binary zero. These "0-char" pairs are read by find
command that takes each zero as an end-of-line mark. The final result is that the %%b
replaceable parameter in the for
command takes each character of the input lines in a very simple way.
This program eliminate exclamation-marks from the input file; this point may be fixed, if needed.
EDIT: Method modified as reply to comments
I modified the original method so it now manages almost all special characters (excepting quote):
2nd EDIT: I further modified the method following a dbenham's advice and it now manages all characters!
@echo off
setlocal DisableDelayedExpansion
(for /F delims^=^ eol^= %%a in (input.txt) do (
set "str=%%a"
set "output= "
for /F delims^=^ eol^= %%b in ('cmd /V:ON /U /C echo !str!^| find /V ""') do (
setlocal EnableDelayedExpansion
for /F "delims=" %%c in ("!output!") do (
endlocal
set "output=%%c %%b"
)
)
setlocal EnableDelayedExpansion
echo !output:~2!
endlocal
)) > output.txt
rem Replace the input file for the result
move output.txt input.txt /Y
input.txt:
1 7<Y>IO
QU|C"K&7
;T Y!9^0
output.txt:
1 7 < Y > I O
Q U | C " K & 7
; T Y ! 9 ^ 0
If the division in lines would not be needed in the output file, a simpler method could be based on this:
set "output="
for /F eol^= %%b in ('cmd /U /C type input.txt ^| find /V ""') do (
set "output=!output! %%b"
)
Upvotes: 2
Reputation: 2666
This can be done. We will use a FOR loop with a subroutine nested inside.
The FOR Loop reads the lines, the LineSpacer
subroutine uses substring syntax to space them out.
@echo OFF
REM - Set your source and destination files.
Set SourceFilePath=C:\Users\Admin\Desktop\SourceFile.txt
Set OutputFilePath=C:\Users\Admin\Desktop\OutputFile.txt
REM Blank out the OutputFile
TYPE nul > %OutputFilePath%
REM For every line in the source text file call linespacer subroutine.
FOR /F "tokens=*" %%A IN (%SourceFilePath%) DO (
SET line=%%A
SETLOCAL EnableDelayedExpansion
SET spacedline=
CALL :LineSpacer
ECHO !spacedline! >> %OutputFilePath%
ENDLOCAL
)
REM - Get the first character of the line, add a space to it
REM - Reset the line variable to one less character, repeat.
:LineSpacer
IF DEFINED line (
SET char=!line:~0,1!
SET "charpluspace=!char! "
SET spacedline=!spacedline!!charpluspace!
SET "line=!line:~1!"
IF "%line%" EQU "!line:~1!" EXIT /b
GOTO linespacer
)
EXIT /b
The batch file above will do what you need. To get a better understanding of how it's done, try some of the examples in the two links I've posted.
Upvotes: 0
Reputation: 49086
Here is a completely commented batch code for this task:
@echo off
setlocal EnableExtensions
rem Define the name of the file to encode by inserting a space
rem character between each alphanumeric character in the file.
set "CodeFile=Test.txt"
rem Define the name of a temporary file with the encoded lines.
set "TempFile=%CodeFile%.tmp"
rem Make sure the temporary file does not already exist.
del "%TempFile%" 2>nul
rem Process each non blank line from file to encode.
for /F "usebackq delims=" %%I in ("%CodeFile%") do call :EncodeLine "%%~I"
rem Overwrite the file to encode with the temporary file
rem with the encoded lines if there was one created at all.
if exist "%TempFile%" move /Y "%TempFile%" "%CodeFile%"
rem Restore previous command environment end exit processing
rem of the batch file without falling through to subroutine.
endlocal
goto :EOF
rem The subroutine EncodeLine inserts a space between each
rem character of the string passed to the subroutine and
rem appends the encoded line to the temporary file.
rem This is done by appending the first character from input
rem line to the current output line after an additionally
rem added space character and removing from input line the
rem just copied first character until the input line does
rem not contain anymore any character.
rem Then output the encoded line without first space character
rem with redirecting the output from handle STDERR to the
rem temporary file with appending the line to temporary file.
rem It is important here to have the redirection operator
rem and the temporary file name on left side before the
rem command ECHO to get correct output if the output line
rem ends with 1 to 9 and avoid adding a trailing space on
rem each output line on writing the line into the file.
rem goto :EOF at end results in exiting the subroutine
rem and returning to FOR command line above.
:EncodeLine
set "InputLine=%~1"
set "OutputLine="
:NextChar
set "OutputLine=%OutputLine% %InputLine:~0,1%"
set "InputLine=%InputLine:~1%"
if not "%InputLine%" == "" goto NextChar
>>"%TempFile%" echo %OutputLine:~1%
goto :EOF
Note: This batch code is not designed for working with any text file contents, just for ANSI encoded text files containing only alphanumeric characters and line terminations.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
echo /?
endlocal /?
for /?
goto /?
if /?
move /?
rem /?
set /?
setlocal /?
See also the Microsoft article Using command redirection operators.
Upvotes: 3
Reputation: 2229
This would be trivial in PowerShell, but batch is a little... errr... messy:
@echo off
setlocal
set INPUT_FILE=c:\temp\in.txt
for /f "tokens=*" %%L in (%INPUT_FILE%) do call :DO_LINE %%L
endlocal
goto END
:DO_LINE
set LINE_IN=%*
set LINE_OUT=
:DL_LOOP
if "%LINE_IN%" == "" echo %LINE_OUT%&&goto END
if "%LINE_OUT%" == "" (
set LINE_OUT=%LINE_IN:~0,1%
) else (
set LINE_OUT=%LINE_OUT% %LINE_IN:~0,1%
)
set LINE_IN=%LINE_IN:~1%
goto DL_LOOP
goto END
:END
You'll need to modify the set INPUT_FILE line.
Good luck!
Upvotes: 0