windows - pantalla - ver comandos
¿Cómo se obtiene la longitud de la cadena en un archivo por lotes? (12)
No parece haber una manera fácil de obtener la longitud de una cadena en un archivo por lotes. P.ej,
SET MY_STRING=abcdefg
SET /A MY_STRING_LEN=???
¿Cómo encontraría la longitud de cadena de MY_STRING
?
Puntos de bonificación si la función de longitud de cadena maneja todos los caracteres posibles en cadenas, incluidos los caracteres de escape, así: !%^^()^!
.
Acabo de encontrar la solución ULTIMATE:
set "MYSTRING=abcdef!%%^^()^!"
(echo "%MYSTRING%" & echo.) | findstr /O . | more +1 | (set /P RESULT= & call exit /B %%RESULT%%)
set /A STRLENGTH=%ERRORLEVEL%-5
echo string "%MYSTRING%" length = %STRLENGTH%
El resultado es:
string "abcdef!%^^()^!" length = 14
Maneja los caracteres de escape, un orden de magnitud más simple que la mayoría de las soluciones anteriores, y no contiene bucles, números mágicos, DelayedExpansion, archivos temporales, etc.
En caso de que el uso fuera del script por lotes (es decir, poner comandos en la consola manualmente), reemplace %%RESULT%%
clave con %RESULT%
.
Si es necesario, la variable %ERRORLEVEL%
se puede establecer en FALSE
utilizando cualquier comando NOP, por ejemplo, echo. >nul
echo. >nul
Como no hay una función incorporada para la longitud de la cadena, puede escribir su propia función así:
@echo off
setlocal
set "myString=abcdef!%%^^()^!"
call :strlen result myString
echo %result%
goto :eof
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
set "s=!%~2!#"
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 "%~1=%len%"
exit /b
)
Esta función necesita siempre 13 bucles, en lugar de una función strlen simple que necesita strlen-loops.
Maneja todos los personajes.
Las primeras líneas son simplemente para demostrar la función: strLen.
@echo off
set "strToMeasure=This is a string"
call :strLen strToMeasure strlen
echo.String is %strlen% characters long
exit /b
:strLen
setlocal enabledelayedexpansion
:strLen_Loop
if not "!%1:~%len%!"=="" set /A len+=1 & goto :strLen_Loop
(endlocal & set %2=%len%)
goto :eof
Por supuesto, esto no es tan eficiente en la versión "13 loop" proporcionada por jeb. Pero es más fácil de entender, y su computadora 3GHz puede pasar por algunas miles de iteraciones en una pequeña fracción de segundo.
Me gusta el enfoque de dos líneas de jmh_gr.
No funcionará con números de un solo dígito a menos que ponga ()
alrededor de la parte del comando antes de la redirección. ya que 1>
es un comando especial "Echo is On" se redireccionará al archivo.
Este ejemplo debería ocuparse de los números de un solo dígito, pero no de los otros caracteres especiales, como <
que pueden estar en la cadena.
(ECHO %strvar%)> tempfile.txt
Prefiero la respuesta aceptada de jeb : es la solución más rápida conocida y la que uso en mis propios scripts. (De hecho, hay algunas optimizaciones adicionales en DosTips, pero no creo que valen la pena)
Pero es divertido crear nuevos algoritmos eficientes. Aquí hay un nuevo algoritmo que usa la opción FINDSTR / O:
@echo off
setlocal
set "test=Hello world!"
:: Echo the length of TEST
call :strLen test
:: Store the length of TEST in LEN
call :strLen test len
echo len=%len%
exit /b
:strLen strVar [rtnVar]
setlocal disableDelayedExpansion
set len=0
if defined %~1 for /f "delims=:" %%N in (
''"(cmd /v:on /c echo(!%~1!&echo()|findstr /o ^^"''
) do set /a "len=%%N-3"
endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b
El código resta 3 porque el analizador hace malabares con el comando y agrega un espacio antes de que CMD / V / C lo ejecute. Se puede prevenir mediante el uso de (echo(!%~1!^^^)
.
Para aquellos que quieren el rendimiento absoluto más rápido posible, la respuesta de jeb puede adoptarse para su uso como un lote "macro" con argumentos . Esta es una técnica avanzada de lotes desarrollada en DosTips que elimina el proceso inherentemente lento de CALLing a: subrutina. Puede obtener más información acerca de los conceptos detrás de las macros de proceso por lotes aquí , pero ese enlace utiliza una sintaxis más primitiva y menos deseable.
A continuación se muestra una macro @strLen optimizada, con ejemplos que muestran las diferencias entre el uso de macro y: subrutina, así como las diferencias en el rendimiento.
@echo off
setlocal disableDelayedExpansion
:: -------- Begin macro definitions ----------
set ^"LF=^
%= This creates a variable containing a single linefeed (0x0A) character =%
^"
:: Define %/n% to effectively issue a newline with line continuation
set ^"/n=^^^%LF%%LF%^%LF%%LF%^^"
:: @strLen StrVar [RtnVar]
::
:: Computes the length of string in variable StrVar
:: and stores the result in variable RtnVar.
:: If RtnVar is is not specified, then prints the length to stdout.
::
set @strLen=for %%. in (1 2) do if %%.==2 (%/n%
for /f "tokens=1,2 delims=, " %%1 in ("!argv!") do ( endlocal%/n%
set "s=A!%%~1!"%/n%
set "len=0"%/n%
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (%/n%
if "!s:~%%P,1!" neq "" (%/n%
set /a "len+=%%P"%/n%
set "s=!s:~%%P!"%/n%
)%/n%
)%/n%
for %%V in (!len!) do endlocal^&if "%%~2" neq "" (set "%%~2=%%V") else echo %%V%/n%
)%/n%
) else setlocal enableDelayedExpansion^&setlocal^&set argv=,
:: -------- End macro definitions ----------
:: Print out definition of macro
set @strLen
:: Demonstrate usage
set "testString=this has a length of 23"
echo(
echo Testing %%@strLen%% testString
%@strLen% testString
echo(
echo Testing call :strLen testString
call :strLen testString
echo(
echo Testing %%@strLen%% testString rtn
set "rtn="
%@strLen% testString rtn
echo rtn=%rtn%
echo(
echo Testing call :strLen testString rtn
set "rtn="
call :strLen testString rtn
echo rtn=%rtn%
echo(
echo Measuring %%@strLen%% time:
set "t0=%time%"
for /l %%N in (1 1 1000) do %@strlen% testString testLength
set "t1=%time%"
call :printTime
echo(
echo Measuring CALL :strLen time:
set "t0=%time%"
for /l %%N in (1 1 1000) do call :strLen testString testLength
set "t1=%time%"
call :printTime
exit /b
:strlen StrVar [RtnVar]
::
:: Computes the length of string in variable StrVar
:: and stores the result in variable RtnVar.
:: If RtnVar is is not specified, then prints the length to stdout.
::
(
setlocal EnableDelayedExpansion
set "s=A!%~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
if "%~2" equ "" (echo %len%) else set "%~2=%len%"
exit /b
)
:printTime
setlocal
for /f "tokens=1-4 delims=:.," %%a in ("%t0: =0%") do set /a "t0=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100
for /f "tokens=1-4 delims=:.," %%a in ("%t1: =0%") do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100
set /a tm=t1-t0
if %tm% lss 0 set /a tm+=24*60*60*100
echo %tm:~0,-2%.%tm:~-2% msec
exit /b
- Muestra de salida -
@strLen=for %. in (1 2) do if %.==2 (
for /f "tokens=1,2 delims=, " %1 in ("!argv!") do ( endlocal
set "s=A!%~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!"
)
)
for %V in (!len!) do endlocal&if "%~2" neq "" (set "%~2=%V") else echo %V
)
) else setlocal enableDelayedExpansion&setlocal&set argv=,
Testing %@strLen% testString
23
Testing call :strLen testString
23
Testing %@strLen% testString rtn
rtn=23
Testing call :strLen testString rtn
rtn=23
Measuring %@strLen% time:
1.93 msec
Measuring CALL :strLen time:
7.08 msec
Puede hacerlo en dos líneas, completamente en un archivo por lotes, escribiendo la cadena en un archivo y luego obteniendo la longitud del archivo. Solo tiene que restar dos bytes para tener en cuenta el CR + LF automático agregado al final.
Digamos que su cadena está en una variable llamada strvar
:
ECHO %strvar%> tempfile.txt
FOR %%? IN (tempfile.txt) DO ( SET /A strlength=%%~z? - 2 )
La longitud de la cadena está ahora en una variable llamada strlength
.
En un poco más de detalle:
-
FOR %%? IN (filename) DO ( ...
FOR %%? IN (filename) DO ( ...
: obtiene información sobre un archivo -
SET /A [variable]=[expression]
: evalúa la expresión numéricamente -
%%~z?
: Expresión especial para obtener la longitud del archivo
Para mezclar todo el comando en una línea:
ECHO %strvar%>x&FOR %%? IN (x) DO SET /A strlength=%%~z? - 2&del x
Quiero adelantar esto diciendo que no sé mucho sobre cómo escribir código / script / etc. pero pensé que compartiría una solución que parece haber surgido. La mayoría de las respuestas aquí me pasaron por alto, así que tenía curiosidad por saber si lo que he escrito es comparable.
@echo off
set stringLength=0
call:stringEater "It counts most characters"
echo %stringLength%
echo.&pause&goto:eof
:stringEater
set var=%~1
:subString
set n=%var:~0,1%
if "%n%"=="" (
goto:eof
) else if "%n%"==" " (
set /a stringLength=%stringLength%+1
) else (
set /a stringLength=%stringLength%+1
)
set var=%var:~1,1000%
if "%var%"=="" (
goto:eof
) else (
goto subString
)
goto:eof
Sí, por supuesto que hay una manera fácil, usando vbscript (o powershell).
WScript.Echo Len( WScript.Arguments(0) )
guardar esto como strlen.vbs
y en la línea de comandos
c:/test> cscript //nologo strlen.vbs "abcd"
Use un bucle for para capturar el resultado (o use vbscript por completo para su tarea de creación de scripts)
Ciertamente es mejor tener que crear soluciones engorrosas utilizando lotes y no hay excusa para no usarlo, ya que vbscript está disponible con cada distribución de Windows (y con powershell en adelante).
Si tienes Windows Vista +, prueba este método de Powershell:
For /F %%L in (''Powershell $Env:MY_STRING.Length'') do (
Set MY_STRING_LEN=%%L
)
o alternativamente:
Powershell $Env:MY_STRING.Length > %Temp%/TmpFile.txt
Set /p MY_STRING_LEN = < %Temp%/TmpFile.txt
Del %Temp%/TmpFile.txt
Estoy en Windows 7 x64 y esto está funcionando para mí.
Solo otro script por lotes para calcular la longitud de una cadena, en solo unas pocas líneas. Puede que no sea el más rápido, pero es bastante pequeño. La subrutina ": len" devuelve la longitud en el segundo parámetro. El primer parámetro es la cadena real que se analiza. Tenga en cuenta que los caracteres especiales deben ser escapados, ese es el caso con cualquier cadena en el archivo por lotes.
@echo off
setlocal
call :len "Sample text" a
echo The string has %a% characters.
endlocal
goto :eof
:len <string> <length_variable> - note: string must be quoted because it may have spaces
setlocal enabledelayedexpansion&set l=0&set str=%~1
:len_loop
set x=!str:~%l%,1!&if not defined x (endlocal&set "%~2=%l%"&goto :eof)
set /a l=%l%+1&goto :len_loop
@echo off
:: warning doesn''t like * ( in mystring
setlocal enabledelayedexpansion
set mystring=this is my string to be counted forty one
call :getsize %mystring%
echo count=%count% of "%mystring%"
set mystring=this is my string to be counted
call :getsize %mystring%
echo count=%count% of "%mystring%"
set mystring=this is my string
call :getsize %mystring%
echo count=%count% of "%mystring%"
echo.
pause
goto :eof
:: Get length of mystring line ######### subroutine getsize ########
:getsize
set count=0
for /l %%n in (0,1,2000) do (
set chars=
set chars=!mystring:~%%n!
if defined chars set /a count+=1
)
goto :eof
:: ############## end of subroutine getsize ########################
@echo off & setlocal EnableDelayedExpansion
set Var=finding the length of strings
for /l %%A in (0,1,10000) do if not "%Var%"=="!Var:~0,%%A!" (set /a Length+=1) else (echo !Length! & pause & exit /b)
establece la var a lo que quieras para encontrar la longitud de la misma o cámbiala a set / p var = para que el usuario la ingrese. Poniendo esto aquí para referencia futura.