Eliminar caracteres de un csv

Estoy siguiendo antiguos post tuyos y he logrado hacer algún avance en lo que necesito.

Tengo unos ficheros .csv con nombre pbx_2020_04_20. Csv, cada día me da uno nuevo y al cual le tengo que eliminar las 3 últimas columnas y como no sabia como hacerlo se me ha ocurrido hacer un .bat que me quite el texto no deseado.

El fichero es de una centralita que me da las llamadas y necesito quitar el final que puede ser (, 27, xxx,), (, 27, xxxx), (, 33, xxx,), (, 33, xxxx,), (, 44, xxx), etc.Te pongo un trocito del texto:

StartTime, duration, caller, callee, cost, price, endpointType, endpointId, company, carrier, ddi, Provider
"2020-04-20 08:33:23", 151,+2000+3491xxxxxx,, 0.0378, User, 608,81,27,755,
"2020-04-20 08:45:36", 16,+2003,+3491xxxxxx,, 0.004, User, 407,52,27,561,
"2020-04-20 08:55:32", 74,+2000,+3462xxxxxx,, 0.0617, User, 1199,52,33,1392,
"2020-04-20 08:59:38", 51,+2001,+3491xxxxxx,, 0.0425, User, 1199,52,27,10041,

Lo que me gustaría hacer es quitar eso y que el fichero salga así:

StartTime, duration, caller, callee, cost, price, endpointType, endpointId, company, carrier, ddi, Provider
"2020-04-20 08:33:23", 151,+2000+3491xxxxxx,, 0.0378, User, 608,81
"2020-04-20 08:45:36", 16,+2003,+3491xxxxx,, 0.004, User, 407,52
"2020-04-20 08:55:32", 74,+2000,+3462xxxxx,, 0.0617, User, 1199,52
"2020-04-20 08:59:38", 51,+2001,+3491xxxxxx,, 0.0425, User, 1199,52

He probado con esto y funciona, pero tengo que poner una linea 'set linea=!linea:^,27,755,^=!' por cada código que quiero eliminar y es que pueden ser cientos

@echo off
Setlocal EnableDelayedExpansion
del /q kkk.csv
for /f "delims=" %%a in ('type *.csv') do (
set linea=%%a
set linea=!linea:^,27,755,^=!
Echo ! Linea! >> Kkk. Csv

Si fuera posible, me gustaría hacer también, que el csv que he modificado se guarde con el nombre del original y el original se renombre al OLD. Es decir OLD_pbx_2020_04_20. Csv para conservar una copia del original.

Si me pudieras ayudar te estaría muy agradecido.

Respuesta
2

Algunos comentarios:

1. Me parece que también deberías eliminar los campos de la cabecera de columna de la primera línea del CSV (en este caso company, carrier, ddi y Provider) por coherencia. Pero no habría problema en respetarlos

2. Intuyo que en el primer registro falta una coma entre "+2000" y "+3491xxxxxx"

3. Parece que el quinto campo siempre está vacío. Si fuera así sería estupendo. Si no siempre lo estuviera habría que darle una vuelta al asunto.

Te paso un bat que puede que se ajuste a tus necesidades. Si no fuera así dímelo y vemos que se puede hacer. Es este:

@echo off
set carpeta=d:\carpeta prueba
set archivo=pbx_2020_04_20.csv
pushd %carpeta%
del /q tmp.csv>nul 2>&1
for /f "tokens=1,2,3,4,5,6,7,8,* delims=," %%a in ('type "%archivo%"') do (
    Echo %%a,%%b,%%c,%%d,,%%e,%%f,%%g,%%h>>tmp. Csv
    )
echo ren %archivo% OLD_%archivo%
Echo ren tmp. Csv %archivo%
Popd

Deberías asignar a la variable CARPETA el path de la carpeta en la que está el archivo a tratar. En cuando a la variable ARCHIVO imagino que no va a ser siempre el mismo. Habría que ver que es lo que te conviene (podría ser "arrastrar" el archivo "encima" del BAT desde el Explorador de Windows, o que el nombre se pueda generar a partir de la fecha de ejecución, o alguna otra manera). En todo caso habría que retocar este BAT para ajustarse a lo que decidas.

Los dos comandos de renombrado (líneas antepenúltima y penúltima) no se van a ejecutar porque están precedidos de ECHO. La idea es ver en primer lugar como sería lo que se va a hacer y si es lo que se quiere ya se podría quitar el ECHO y forzar la ejecución del comando.

Si lo que digo en el punto 3 no es válido habrá que repensarse el bat porque las variables vacías no se tratan con facilidad.

El editor de la página, fiel a sus malos hábitos, ha incluido espacios delante de CSV en algunos casos (líneas 7 y 10 del BAT). Debes quitar esos espacios antes de probarlo.

Muchas gracias por el programa.

En el punto 1, tienes razón, debería desaparecer las columnas.

En el punto 2, como bien indicas faltaba una coma "," entre el +2000 y +91xxxxxx. 

A la carpeta le he puesto el nombre CSV,  (d:/CSV) que es donde se almacenan los ficheros.

Lo he probado, quitando el espacio, pero me falla, en parte porque he cometido un error grave, ya que hay un campo id que no había copiado y el fichero original csv sería así:

id,StartTime, duration, caller, callee, cost, price, endpointType, endpointId, company, carrier, ddi, Provider
12,"2020-04-20 08:33:23", 151,+2000,+3491xxxxxx,, 0.0378, User, 608,81,27,755,
13,"2020-04-20 08:45:36", 16,+2003,+3491xxxxxx,, 0.004, User, 407,52,27,561,
14,"2020-04-20 08:55:32", 74,+2000,+3462xxxxxx,, 0.0617, User, 1199,52,33,1392,
15,"2020-04-20 08:59:38", 51,+2001,+3491xxxxxx,, 0.0425, User, 1199,52,27,10041,

Del punto 3, el campo quinto que ahora sería el sexto puede que en algún caso tenga valor, por ejemplo las llamadas a 902 que tienen coste de establecimiento. Además he comprado, que, en la primera fila me desaparece el nombre del campo 6. He intentado retocar el fichero pero lo único que consigo es que me quite en cost las dos comas ",," y me lo deja en 1 coma ",", pero no es lo que necesito.

El último campo también está vació, pero no sé si en algún momento tendrá un valor y dejaría de funcionar el script.

También tienes razón en lo del nombre del fichero, cada día tiene un nombre:

 pbx 2020_04_19.csv,  pbx 2020_04_20.csv que son las llamadas del día anterior.

Los ficheros son descargados por un ftp cada día a esta carpeta csv automáticamente. Yo había pensado hacer una tarea programada para que ejecutara el bat y que luego mover los archivos a otras carpetas una vez parseados. No sé si me explico.

De todas formas con lo que has hecho me has ayudado mucho. Muchísimas gracias

Prueba con esta nueva versión. Ya debe ser capaz de tratar adecuadamente el valor no vacío en el sexto campo. Y no debe haber problemas por el hecho de que el último campo no esté vacío, porque los campos posteriores al décimo se desechan.

@echo off
Setlocal EnableDelayedExpansion
set carpeta=d:\carpeta prueba
set archivo=pbx_2020_04_20.csv
pushd %carpeta%
del /q tmp.csv>nul 2>&1
for /f "tokens=* delims=" %%x in ('type "%archivo%"') do (
   set linea=%%x
   set linea=!linea:,,=, ,!
   for /f "tokens=1,2,3,4,5,6,7,8,9,10,* delims=," %%a in ("!linea!") do (
      if "%%f"==" " (echo %%a,%%b,%%c,%%d,%%e,,%%g,%%h,%%i,%%j>>tmp.csv
         ) Else echo %%a,%%b,%%c,%%d,%%e,%%f,%%g,%%h,%%i,%%j>>tmp. Csv
      )
   )
echo ren %archivo% OLD_%archivo%
Echo ren tmp. Csv %archivo%
Popd

Ahora me pondré con la generación automática del nombre del archivo a partir de la fecha de hoy. Me parece correcto tu plan de hacerlo mediante una tarea programada.

Creo que ya lo tengo. Sería este:

@echo off
Setlocal EnableDelayedExpansion
set carpeta=d:\CSV
call :nomarch
echo el archivo se llama %archivo%
pushd %carpeta%
del /q tmp.csv>nul 2>&1
for /f "tokens=* delims=" %%x in ('type "%archivo%"') do (
   set linea=%%x
   set linea=!linea:,,=, ,!
   for /f "tokens=1,2,3,4,5,6,7,8,9,10,* delims=," %%a in ("!linea!") do (
      if "%%f"==" " (echo %%a,%%b,%%c,%%d,%%e,,%%g,%%h,%%i,%%j>>tmp.csv
         ) Else echo %%a,%%b,%%c,%%d,%%e,%%f,%%g,%%h,%%i,%%j>>tmp. Csv
      )
   )
ren %archivo% OLD_%archivo%
Ren tmp. Csv %archivo%
Popd
goto :EOF
:nomarch
call :diaant %date% -1
set archivo=pbx_%aaaa%_%mm%_%dd%.csv
goto :EOF
:diaant
call :dias %1
set /a j+=%2
call :inc %j%
goto :EOF
:dias
for /f "tokens=1,2,3 delims=-/." %%a in ("%1") do (
   set dd=%%a&set mm=%%b&set aaaa=%%c)
set /a dd=100%dd%%%100,mm=100%mm%%%100
set /a z=14-mm,z/=12,a=aaaa-z,m=mm+12*z-3,j=153*m+2
set /a j=j/5+dd+a*365+a/4-a/100+a/400-719469
goto :EOF
:inc
set /a a=%1+719468,b=4*a+3,b/=146097,c=-b*146097,c/=4,c+=a
set /a d=4*c+3,d/=1461,e=-1461*d,e/=4,e+=c,m=5*e+2,m/=153,dd=153*m+2,dd/=5
set /a dd=-dd+e+1,mm=-m/10,mm*=12,mm+=m+3,aaaa=b*100+d+m/10
(if %mm% LSS 10 set mm=0%mm%)&(if %dd% LSS 10 set dd=0%dd%)
goto :EOF

Como es posible que el editor de la página haga alguna de las suyas lo revisaré después de enviarla y se encuentro algún error mandaré otro mensaje. La complicación de las rutinas DIAS e INC tiene que ver con el hecho de que cada 400 años se repita la secuenciación de las fechas. Estas rutinas están inspiradas en las que hace  bastante tiempo puso José Manuel Tella Llop ([email protected]) en la página http://multingles.net/docs/jmt/difecha.htm. (que ya no existe). Puedes comprobar que funcionan y que no tienen problemas ni con los años bisiestos ni con el cambio de mes o de año. El valor -1 que aparece en la instrucción:

call :diaant %date% -1

Tiene que ver con que se quiera la fecha del día anterior. Si se quisiera otra fecha hacia delante o hacia atrás bastaría con poner el número de días respecto a la fecha actual.

Prueba y me comentas.

Creo que el editor solo ha puesto alguna mayúscula (que no tiene importancia) y ha incluido un espacio antes de CSV en un par de líneas que tienen que ver con el archivo auxiliar TMP. CSV

Gracias de nuevo,

He probado la primera parte y funciona casi perfecto, lo único que cuando he parseado el fichero entero he visto unos errores debido a lo siguiente. En llamadas que han sido desviadas no me aparece el campo 8 y el 9 y salen 3 comillas juntas ",,," , es decir, el campo está  vacío y el script me devuelve una coma y un espacio y otra coma ", ,". El es la única cosa que he visto en todo el fichero que fallaba.

id,StartTime, duration, caller, callee, cost, price, endpointType, endpointId, company, carrier, ddi, Provider
12,"2020-04-20 08:33:23",151,+2000+3491xxxxxx,, 0.0378, User, 608,81,27,755,
13,"2020-04-20 08:45:36",16,+2003,+3491xxxxxx,, 0.004, User, 407,52,27,561,

23,"2020-04-20 09:36:42",46,+2000,+3467xxxxxx,,0.0384,,,27,430,

la parte del nombre aún no la he probado, quiero probar que esta me funcione bien.

muchas gracias de nuevo

Si te entiendo bien creo que me estás diciendo que no es el sexto campo el único que puede aparecer vacío. Si es así podemos hacer dos cosas según podamos o no decidir que campos pueden estar vacíos. Si no podemos (o no queremos) decidir que campos pueden estar vacíos habría que trabajar con la posibilidad de que cualquier campo pueda estar vacío con lo que trataríamos de uno en uno todos los campos e iríamos formando la línea de salida según ese tratamiento. Sería más lento pero no creo que resultara demasiado complicado.
El tratamiento actual toma la línea como un todo y sustituye en ella las dos comas sucesivas por la combinación coma-espacio-coma porque el FOR del BAT no permite valores vacíos en las variables. Como luego solo revisa el sexto campo para restituir el espacio por el vacío (es decir ",," por ",,") en los otros campos sacaría la combinación coma-espacio-coma. De todas formas he visto que el caso de la triple coma no lo trata como pensaba (que sería sustituirlo por ",,,") por lo que habría que ver si hay que hacer una doble pasada de sustitución.

De todas formas veo que en este otro fragmento te has vuelto a "comer" la coma entre el +2000 y el +3491xxxxxx del primer registro

En principio por los ficheros que he revisado serian solo esas tres columnas las que pueden ir vacías, no obstante, creo que puede ser conveniente que se comprobaran todas por si acaso aunque fuera algo más lento el resultado. Tampoco es que tenga prisa que tarde unos segundos o unos minutos.

Vuelves a tener razón en lo de la  coma ","  del +2000, lo copie del primer fichero.

Gracias de nuevo por tu ayuda y las explicaciones, que me hacen entender lo que estamos haciendo.

Un saludo

Creo que este bat ya estaría cerca de lo que necesitas. Ya pretende incluir el tratamiento de todos los campos vacíos. También contempla que el archivo a tratar no haya sido creado (por un fallo en el FTP, por ejemplo):

@echo off
set carpeta=d:\CSV
call :nomarch
rem echo el archivo se llama %archivo%
rem set archivo=pbx_2020_04_20.csv
pushd %carpeta%
if not exist %archivo% echo No existe el archivo %archivo%, se aborta el proceso&pause&goto :EOF
del /q tmp.csv>nul 2>&1
for /f "delims=" %%x in ('type "%archivo%"') do call :tratlin %%x
ren %archivo% OLD_%archivo%
ren tmp.csv %archivo%
popd
goto :EOF
:tratlin
set linent=%*
:doblecoma
echo %linent% | find ",,">nul&&(set linent=%linent:,,=,@,%&goto :doblecoma)
for /f "tokens=1,2,3,4,5,6,7,8,9,10,* delims=," %%a in ("%linent%") do call :salida %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j
goto :EOF
:salida
set linsal=
:bucle
if #%1#==## echo %linsal:~0,-1%>>tmp.csv&goto :EOF
if #%1#==#@# (set linsal=%linsal%,
   ) else set linsal=%linsal%%1,
shift
goto :bucle
:nomarch
call :diaant %date% -1
set archivo=pbx_%aaaa%_%mm%_%dd%.csv
goto :EOF
:diaant
call :dias %1
set /a j+=%2
call :inc %j%
goto :EOF
:dias
for /f "tokens=1,2,3 delims=-/." %%a in ("%1") do (
   set dd=%%a&set mm=%%b&set aaaa=%%c)
set /a dd=100%dd%%%100,mm=100%mm%%%100
set /a z=14-mm,z/=12,a=aaaa-z,m=mm+12*z-3,j=153*m+2
set /a j=j/5+dd+a*365+a/4-a/100+a/400-719469
goto :EOF
:inc
set /a a=%1+719468,b=4*a+3,b/=146097,c=-b*146097,c/=4,c+=a
set /a d=4*c+3,d/=1461,e=-1461*d,e/=4,e+=c,m=5*e+2,m/=153,dd=153*m+2,dd/=5
set /a dd=-dd+e+1,mm=-m/10,mm*=12,mm+=m+3,aaaa=b*100+d+m/10
(if %mm% LSS 10 set mm=0%mm%)&(if %dd% LSS 10 set dd=0%dd%)
goto :EOF

Por cierto que no me cuadra el registro:

23,"2020-04-20 09:36:42", 46,+2000,+3467xxxxxx,, 0.0384,,,27,430,

Me sale que tiene un campo menos (11 en lugar de 12)

Me olvidaba decirte que si quieres poner cosas en una ventanita como las que pongo yo (que suele agradecerse para que resulten más legibles) solo tienes que usar el icono "<>" (Snippet o "Insertar código fuente") de la barra de herramientas y copiar en la ventanita emergente lo que te parezca.

Funciona a la perfección. Eres un genio, lo malo es que al hacerlo tan bien me has dado una cosa que había tenido en cuenta hasta que me has hecho el fichero "si no encuentra el archivo" que alguna vez me ha pasado. Por algún motivo no se ha descargado el .csv del ftp y cuando me he dado cuenta a lo mejor han pasado dos o tres días y le fuerzo yo la descarga y me encuentro con dos o más ficheros a la vez.

Con este programa, entonces me encontraría con el problema que no podría pasar los 3 a la vez, solo me cogería el del día actual.

Como te he dicho, me has ayudado muchísimo y no es necesario volverte más loco. Lo que haré si eso ocurre, es crear otro .bat que no tenga el nombre del fichero y ejecutarlo 1 por 1. No es una cosa que ocurra muy frecuentemente y me tocará revisarlo.

Te lo agradezco un millón de veces, te mereces un 10 por la rapidez en la respuesta y el excelente trabajo, además de haberte tomado la molestia en explicarme el funcionamiento.

Un saludo y te deseo lo mejor

Siempre puedes descomentar (borrar el REM) la línea que dice

rem set archivo=pbx_2020_04_20.csv

para poner, "a pelo", el nombre del archivo que quieres tratar. Como este comando está después del que calcula el nombre prevalece sobre lo otro (digamos que le has hecho trabajar en vano).

Una idea Genial, si te pudiera dar más puntos dime como que lo hago.

saludos

No te preocupes. Aunque mi vanidad agradece las buenas "críticas" (y a veces pueda pensar que algunos interlocutores no son todo lo agradecidos que deberían) en el fondo con lo que disfruto es con el proceso de "fabricar" el guión/script/programa que hace lo que se me pide. Y la "programación" batch de Windows/MSDOS es lo suficientemente retorcida, a veces, como para asegurar la "diversión" en cuanto el problema a resolver se salga ligeramente de lo habitual. En ese sentido la otra shell de Windows, Powershell, es mucho más potente, creo que es "orientada a objetos" y no tiene las "peculiaridades" de esta shell (CMD/MSDOS/Símbolo del sistema). Pero a mí me divierte bastante. De modo que puedes estar seguro de que he disfrutado mucho y ahí está mi mayor recompensa.

Y si alguna vez crees que puedo ayudarte no dudes en intentarlo.

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas