alguien se le ocurre como resolver este problema.

505 views
Skip to first unread message

carlosmupe

unread,
Jan 8, 2018, 3:28:03 PM1/8/18
to Comunidad de Visual Foxpro en Español
Hola estimados, tengo el siguiente problema, se trata de un archivo de texto que nos entrega un proveedor con 80 columnas cada dato separado por punto y comma ";", sea numérico o caracater.

algo como esto:

dato1;dato2;dato3;dato4;dato5;.......dato80;CR LF
dato1;dato2;dato3;dato4;dato5;.......dato80;CR LF
dato1;dato2;dato3;dato4;dato5;.......dato80;CR LF

hasta aquí todo bien, el problema se genera cuando en algunos de los campos dato(1...80) que son textos de descripción, vienen saltos de carro como ENTER, entonces al realizar un append from, el archivo queda corrido completo ya que reconoce el ENTER como fin de linea.
También he recorrido el archivo abriendo en bajo nivel y preguntando si vienen las 80 columnas en otro caso posiblemente la linea tenga un termino de carro en algún dato.

actualmente los archivos los reviso a mano con notepad++ visualmente es fácil darse cuenta cuando viene un ENTER y se corrige eliminándolo manualmente, pero me gustaría automatizar esta eliminación.

cabe destacar que los archivos son grandes, en promedio 320 mil lineas por archivo, con  un peso de 330 MB aprox.

a alguien se le ocurre como automatizar esta eliminación?

saludos y gracias

Víctor Hugo Espínola Domínguez

unread,
Jan 8, 2018, 5:09:32 PM1/8/18
to publice...@googlegroups.com
1- Vuelca el archivo a una variable
2- Reemplaza CRLF por LFLF
3- Reemplaza CR por ""
4- Reemplaza LFLF por CRLF
5- Devuelve la variable al archivo

Saludos,
Víctor.
Lambaré - Paraguay.

HernanCano

unread,
Jan 8, 2018, 6:09:36 PM1/8/18
to Comunidad de Visual Foxpro en Español
Perdón, Víctor:
¿Acaso los "Enter normales" (los que no hacen parte de la pregunta de Carlos) no se dañan también?

Víctor Hugo Espínola Domínguez

unread,
Jan 8, 2018, 7:14:15 PM1/8/18
to publice...@googlegroups.com
El proceso que indiqué es para eliminar todos los CHR(13) que no forman parte del final de línea, es decir para respetar los CR LF

Saludos,
Víctor.
Lambaré - Paraguay.


carlosmupe

unread,
Jan 9, 2018, 7:03:35 AM1/9/18
to Comunidad de Visual Foxpro en Español
Gracias estimados por sus respuestas la verdad también intente realizar el reemplazo de los ENTER, el problema es que una variable no aguanta el almacenamiento total del contenido del archivo ya que pesa 330 MB aprox.

quizás tenga que leer linea a linea el archivo e ir traspasando a uno nuevo limpiando y concatenando los datos.

saludos y gracias

Fernando D. Bozzo

unread,
Jan 9, 2018, 3:02:55 PM1/9/18
to Comunidad de Visual Foxpro en Español
Hola Carlos:

¿Y no hay posibilidad de pedirle al proveedor un formato estándar en vez de uno inventado?
Se beneficiarían todos sus clientes, y por ahí nadie se lo dijo.

carlosmupe

unread,
Jan 9, 2018, 3:10:38 PM1/9/18
to Comunidad de Visual Foxpro en Español
La misma pregunta me hice la primera vez lamentablemente no pagamos por la entrega de esta información, nos la entregan de buena onda, por ende no podemos exigir mucho. El formato esta definido son 80 campos separados por ;, lamentablemente no siempre se cumple con ello.

saludos y gracias

Fernando D. Bozzo

unread,
Jan 9, 2018, 4:24:58 PM1/9/18
to Comunidad de Visual Foxpro en Español
Veo un problema grande:

Si los CR/LF pueden estar en cualquier sitio y "no siempre" se cumple que haya 80 campos separados por ";"... entonces termina siendo una tarea de adivinación y no un proceso automatizado.

Igualmente, entregar información así, de cualquier manera, no es muy buena onda que digamos.

Fidel Charny

unread,
Jan 9, 2018, 5:20:37 PM1/9/18
to Comunidad de Visual Foxpro en Español
Como dice Fernando, si no siempre existen los separadores chr(59), habrá que adivinar.
Si la alteración consiste en que existen CR y/o LF intercalados, se puede encontrar una solución, recreando el archivo en bajo nivel.
En el ejemplo que sigue creo un archivo ensayo que tiene esos errores y en la segunda parte se crea un archivo new con el formato corregido.
Condiciones:
 a) que existan todos los separadores CHR(59)
 b) que todos los campos terminen con dicho separador, de tal modo que correspondan 80 separadores por línea.

LOCAL lcFileNew,lcFileName,;
    lcText
,lcAlterText,;
    i
,j,;
    lnHandle
,lnNewHandle,;
    lnLines
,lnField,;
    lcLine
   
*<Crear_Ensayo>
    lcFileName
= ADDBS(FULLPATH(""))+"de_buena_onda.txt"
    lcFileNew
= ADDBS(FULLPATH(""))+"de_buena_onda_new.txt"
    lcText
= ""
    lcAlterText
= ""
    lcFields
= "4,12,32,40,78,80"
    lnLines
= ALINES(laFields,m.lcFields,1+4,CHR(44))
    FOR i
= 1 TO m.lnLines
        lnField
= VAL(laFields[i])
        FOR j
= 1 TO lnField
            lcAlterText
= m.lcAlterTExt + TRANSFORM(j) + CHR(59)
        NEXT
        lcAlterTExt
= m.lcAlterText + CHR(13)
        FOR j
= m.lnField + 1 TO 80
            lcAlterText
= m.lcAlterText + TRANSFORM(j) + CHR(59)
        NEXT
        lcAlterText
= m.lcAlterTExt + CHR(13) + CHR(10)
    NEXT

    FOR j
= 1 TO 5
        FOR i
= 1 TO 80
            lcText
= m.lcText + TRANSFORM(i)+CHR(59)
        NEXT
        lcTExt
= m.lcText + CHR(13) + CHR(10)
    NEXT
    lcTExt
= m.lcTExt ;
       
+ m.lcAlterTExt ;
       
+ m.lcText

    STRTOFILE
(m.lcText,m.lcFileName)
*</Crear_Ensayo>

*<Modificar_Archivo>
    lnNewHandle
= FCREATE(m.lcFileNew)
    lnHandle
= FOPEN(m.lcFileName)
    lcLine
= ""
    DO WHILE
!FEOF(m.lnHandle)
        lcLine
= m.lcLine + FGETS(m.lnHandle,8192)
        lnOccurs
= OCCURS(CHR(59),m.lcLine)
        IF m
.lnOccurs = 80
            FPUTS
(m.lnNewHandle,lcLine)
            lcLine
= ""
        ENDIF
    ENDDO
   
= FCLOSE(m.lnHandle)
   
= FCLOSE(m.lnNewHandle)
*</Modificar_Archivo>

francisco prieto

unread,
Jan 9, 2018, 5:41:39 PM1/9/18
to publice...@googlegroups.com
Carlos para archivos extremadamente grandes usa Fopen y Fread y la solucion de Victor...
Tendras que hacer 2 pasadas...
La primera para formatear correctamente el archivo.
La segunda para importarlo.

Saludos,

Pancho
Córdoba
Argentina

francisco prieto

unread,
Jan 9, 2018, 5:49:28 PM1/9/18
to publice...@googlegroups.com
Carlos te hago una aclaracion mas...

Abris el archivo con Fopen...
Luego vas leyendo digamos de a 100 caracteres y reemplazando según la técnica de Victor...
Una vez que la sarta de 100 caracteres esta normalizada la vas escribiendo en otro archivo con FWrite o bien con strtofile.
Cuando tenes escrito todo el archivo... cerras ambos archivos e importas el normalizado...

Esa técnica de lectura se utiliza sobre todo al comunicarte con un dispositivo externo como ser una balanza...

Saludos,

Pancho
Córdoba
Argentina

HernanCano

unread,
Jan 9, 2018, 8:19:47 PM1/9/18
to Comunidad de Visual Foxpro en Español
Sí, Víctor. Gracias.
Observo que tu segundo punto "pretende" "respetar" los CRLF.
Entonces concluyo que (en un memo) los "enter" están compuestos por esos dos caracteres.
Termina la duda mía.
Chao.

HernanCano

unread,
Jan 9, 2018, 8:28:40 PM1/9/18
to Comunidad de Visual Foxpro en Español
Carlos:
Entiendo entonces que tu escenario es "problemático", pero al menos tienes algo que se cumple siempre: los campos están separados por punto y coma.

Te recomiendo entonces un viaje --en la ayuda de VFP-- por el ejemplo de la función STREXTRACT(). Es la solución a tu impasse.

Aunque la propuesta de Fidel es ingeniosa.

Nos cuentas.

Fernando D. Bozzo

unread,
Jan 10, 2018, 1:16:56 AM1/10/18
to publice...@googlegroups.com
Yo creo que haga lo que haga ek problema es el mismo:

El formato esta definido son 80 campos separados por ;, lamentablemente no siempre se cumple con ello

Si "no siempre" tiene los 80 campos, es imposible saber siempre donde termina un campo, ya que si en algún caso tiene 65 se estarán tomando los 15 siguientes como parte del mismo registro. 

francisco prieto

unread,
Jan 10, 2018, 7:30:56 AM1/10/18
to publice...@googlegroups.com
Es lo mismo por exactitud Fernando, porque los tipos que le enviaron el archivo lo hacen de mala gana, pero el puede procesar el archivo y generar un informe de los registros que no seran tomados en cuenta... y luego que quede pegado quien sea el responsable del envio de ese archivo.

Saludos,

Pancho
Cordoba
Argentina

carlosmupe

unread,
Jan 10, 2018, 7:34:44 AM1/10/18
to Comunidad de Visual Foxpro en Español
Estimados Muchas Gracias por todos sus aportes, efectivamente el tema siempre es complejo ya que mes a mes espero que el archivo venga consistente no siempre ocurre :), efectivamente siempre los datos vienen separados por ; y traen un encabezado de 80 campos, muchas veces algunas lineas también traen 81 o 82 campos me imagino que se el proceso que genera estas salidas genera uno o dos ; mas adicional lo que produce tener 2 o 3 campos mas, en definitiva yo solo tomo los 80 primeros, el resto asumo que vienen vacios ya que no están declarados en el encabezado, revisando el archivo también me di cuente que algunas lineas traían LN en los datos de texto.

Cree este código y hasta el momento me ha funcionado bien, revisa linea x linea y verifica que cada una tenga los 80 o mas campos si es asi lo traspasa a uno nuevo, en otro caso asume que la continuación de sus lineas están mas abajo y las almacena hasta cumplir con las 80 o mas.


LOCAL L_nfile,L_nSize
L_nfile=FOPEN(L_Archivotxt,0)
L_nSize=FSEEK(L_nfile, 0, 2)
IF L_nSize <= 0
   =MESSAGEBOX("El Archivo Seleccionado:"+L_Archivotxt+", esta Vacio",16,"Error")
   =FCLOSE(L_nfile)
   RETURN
ENDIF

LOCAL L_laLinea,L_TotalElementos,L_NumeroCampos
LOCAL ARRAY L_ArrayElementos(1)
STORE 0 TO L_numeroCampos
=FSEEK(L_nfile, 0, 0)
TRY
DO WHILE !FEOF(L_nfile)
   L_laLinea=FGETS(L_nfile,8192)
   L_TotalElementos=ALINES(L_ArrayElementos,L_laLinea,";")
   IF (L_TotalElementos>=80)
      STRTOFILE(L_laLinea+crlf,L_ArchivoSalida,.t.)
   ELSE
      thisform.edit1.Value=thisform.edit1.Value+L_laLinea
      L_numeroCampos=L_numeroCampos+L_TotalElementos
      IF L_numeroCampos>=80
         STRTOFILE(thisform.edit1.Value+crlf,L_ArchivoSalida,.t.)
         thisform.edit1.Value=""
         L_numeroCampos=0
      ENDIF
   ENDIF
ENDDO
IF !EMPTY(thisform.edit1.Value)
    STRTOFILE(thisform.edit1.Value+crlf,L_ArchivoSalida,.t.)
ENDIF

CATCH TO oErr
   =MESSAGEBOX("Error:"+oErr.Message+CHR(13)+"El Formato del Archivo Puede ser Incorrecto.",16,"Error")
ENDTRY
=FCLOSE(L_nfile)

carlosmupe

unread,
Jan 10, 2018, 3:36:46 PM1/10/18
to Comunidad de Visual Foxpro en Español
Amigos solo para ponerle mas dificultad al problema me di cuenta de algunos detalles no menores, el codigo que adjunte funciona en algunos casos en otros no.

encontré que en el archivo algunos datos vienen con esta estructura

numero1;numero2;"texto;mas texto;LN";numero3..............CRLN
numero1;numero2;"texto;mas texto;CRLN";numero3..............CRLN

me di cuenta que se parece mucho al formato de un archivo csv así que le cambie la extensión y lo abrí en excel sin problemas.

1 .- Mi problema ahora es que el carácter ";" no es delimitador de campo ya que en los datos de texto también pueden venir ";" y eso provoca que el arreglo genere mas de 80 columnas.
2.- Intente importarlo con el wizard de foxpro, pero figura corrido por los LN O CRLN que traen los campos de texto.
3.- Intente hacer un append from obteniendo el mismo resultado que el punto 2
4.- Intente pasar el resultado a una variable y realizar los replace tal como me indico Victor, pero el tamaño del archivo me acusa de insuficiente memoria.
5.- Guarde el csv en formato xlsx, pero al traspasar de xlsx a dbf con rutinas puestas en el mismo foro se cuelga, posiblemente por numero de registros.

saludos y gracias por todo

Fidel Charny

unread,
Jan 10, 2018, 4:57:24 PM1/10/18
to Comunidad de Visual Foxpro en Español
Si los campos texto están calificados por chr(34), puedes usar una rutina como la siguiente (suponiendo que los campos son 80).
Si los campos, después de eliminar los falsos separadores, fueran realmente más de 80, se justificará utilizar ALINES() para recrear la linea a insertar en el archivo nuevo tomando solamente los primeros 80 campos.
En este ejemplo, los CHR(59) que se encuentren entre comillas dobles (supuestamente campos de texto) se reemplazan por CHR(46).

Conviene tener presente que la función FGETS() descarta los caracteres CHR(10).
Para el caso me parece bastante más práctico y más rápido utilizar funciones de bajo nivel para escribir el archivo reprocesado.
Por otra parte no veo la necesidad de utilizar ALINES() cuando se trata solamente de saber cuántos caracteres CHR(59) tiene un texto. Para eso tenemos la función OCCURS() que te evita crear un array que no tiene ningún uso posterior.
PROCEDURE Reprocess_txt
LPARAMETERS tcFile_Original

TRY
    LOCAL i
,;
        lcChar
,;
        lcFile_reprocessed
,;
        lcLine
,;
        lcLineExtract
,;
        lDoubleQuotes
,;
        lnHandle
,;
        lnNewHandle
,;
        loex AS
Exception

   
    lcFile_reprocessed
= FORCEEXT(ADDBS(JUSTPATH(m.tcFile_Original)) ;
               
+ JUSTSTEM(m.tcFile_Original)+"_rep",JUSTEXT(m.tcFile_Original))


    lnNewHandle
= FCREATE(m.lcFile_reprocessed)
    lnHandle
= FOPEN(m.tcFile_Original)
    lcLine
= ""
    lDoubleQuotes
= .f.
    DO WHILE
!FEOF(m.lnHandle)
        lcLineExtract
= FGETS(m.lnHandle,8192)
        FOR i
= 1 TO LEN(m.lcLineExtract)
            lcChar
= SUBSTR(m.lcLineExtract,i,1)
            IF m
.lcChar = CHR(34)
                lDoubleQuotes
= !m.lDoubleQuotes
            ENDIF
            IF lDoubleQuotes AND m
.lcChar = CHR(59)
                lcChar
= CHR(46)
            ENDIF
            lcLine
= m.lcLine + m.lcChar
        NEXT
        lnOccurs
= OCCURS(CHR(59),m.lcLine)
        DO CASE
            CASE m
.lnOccurs = 80
                FPUTS
(m.lnNewHandle,lcLine)
                lcLine
= ""            
            CASE m
.lnOccurs > 80
               
                lnLines
= ALINES(laLines,m.lcLine,16,CHR(59))
                lcLine
= ""
                FOR i
= 1 to 80
                    lcLine
= m.lcLine + laLines[m.i]
                NEXT
                FPUTS
(m.lnNewHandle,lcLine)
           
                lcLine
= ""
            OTHERWISE
               
* lcLine conserva su valor
               
* para juntar la linea siguiente.
        ENDCASE
    ENDDO

               
CATCH TO LOEX
    LOEX
.USERVALUE = PROGRAM()
   
*SHOWERROR(LOEX) && rutina que muestra el error
FINALLY
   
= FCLOSE(m.lnHandle)
   
= FCLOSE(m.lnNewHandle)
ENDTRY    
 
RETURN m
.lcFile_reprocessed


HernanCano

unread,
Jan 10, 2018, 8:57:14 PM1/10/18
to Comunidad de Visual Foxpro en Español
Carlos:
Se evidencia un escenario más problemático todavía.

Insisto en que al menos tienes algo que se cumple siempre: los campos están separados por punto y coma.... y bueno: los campos de caracteres están encerrados por comillas.
----------------------------------------
En tu escenario no puedes utilizar procedimientos standard, pues existen demasiadas deficiencias en el archivo que recibes.

Insisto en que deberías utilizar STREXTRACT() buscando el punto y coma como separador por defecto, pero si se encuentra unas comillas dobles, entonces debe buscar hasta las comillas------------- y quitando los chr(13) y/o chr(10).

Insisto: el ejemplo de la ayuda de STREXTRACT es preciso: cambiarías 

IF RIGHT(cTagName,1) = '/'

por

IF LEFT(cTagName,1) == '"'

Pero evidentemente debes revisar todo el ejemplo... pues yo no he hecho la tarea.... sólo te explico los puntos importantes.

HernanCano

unread,
Jan 10, 2018, 9:19:37 PM1/10/18
to Comunidad de Visual Foxpro en Español
Supongo que cuando escribes LN te refieres a LF.

carlosmupe

unread,
Jan 11, 2018, 7:02:02 AM1/11/18
to Comunidad de Visual Foxpro en Español
Si estimado es LF (Line Feed)

francisco prieto

unread,
Jan 11, 2018, 7:02:06 PM1/11/18
to publice...@googlegroups.com
Carlos,

Podrias reemplazar... LN por CRLN, luego CRCR por CR y finalmente LNLN por LN y ya tendrias el problema resuelto.

Saludos,

Pancho
Cordoba
Argentina
Reply all
Reply to author
Forward
0 new messages