Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   intercalar texto en un archivo (https://www.clubdelphi.com/foros/showthread.php?t=86233)

aguml 04-07-2014 00:35:32

intercalar texto en un archivo
 
hola amigos, tengo que hacer una aplicacion que abra un archivo y busque la posicion de una cadena y a partir de ahi tiene que insertar n texto. Ese texto a insertar, para obtenerlo, tiene que abrir otro archivo, buscar la misma cadena que en el otro para saber desde donde empezar y buscar otra cadena mas para saber hasta donde. Lo intento explicar mejor, en el archivo A buscaria la cadena <body> para insertar a partir de ahi el texto que obtendrá del archivo B. En B busca <body> y donde lo encuentre sera el inicio de la cadena a copiar. Luego en B buscará </body> y en el lugar que lo encuentre ese será el final. El siguiente paso seria insertar lo obtenido en B dentro de A entre <body> y </body>. He pensado que podria usar un par de TStringList Para hacerlo pero no se me ocurre la manera. ¿Me podeis dar unas pautas y decirme que metodos usar para cada cosa o si lo hariais de otro modo? Las principales dudas son ¿Como busco esas etiquetas? Tengo entendido que Find es para listas ordenadas y solo se me ocurre usar un for y .pos para buscar linea a linea. ¿Como inserto un bloque entero? ¿Tengo que insertar linea por linea? ¿Como borro solo parte de una linea? Se me ocurre usar un ansistring auxiliar pero supongo que habrá formas mejores. Necesito modificar asi cientos de archivos y por eso he pensado en crear una herramienta que lo haga por mi. Perdon por ponerlo todo de corrido pero desde el movil no me deja poner saltos de linea.

escafandra 04-07-2014 10:01:01

Te respondo sin hacer uso de la VCL, sólo con API de windows

Código:

int Search(char *Buffer, char *S, int SizeBuffer)
{
  for(int n= 0; n<SizeBuffer-lstrlen(S); n++){
    if(Buffer[n] == *S){
      int i=1;
      for(; S[i]; i++)
        if(Buffer[n+i]!=S[i])  break;
      if(i==lstrlen(S)) return n;
    }
  }
  return -1;
}

void Inserta(char *FileNameA, char *FileNameB)
{
  HANDLE hFileA = CreateFile(FileNameA, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0);
  if(hFileA!=(HANDLE)(-1)){
    HANDLE hFileB = CreateFile(FileNameB, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0);
    if(hFileB!=(HANDLE)(-1)){

      DWORD SizeA = GetFileSize(hFileA, 0);
      DWORD SizeB = GetFileSize(hFileA, 0);

      char* BufferA = new char[SizeA];
      char* BufferB = new char[SizeB];
      _lread((int)hFileA, BufferA, SizeA);
      _lread((int)hFileB, BufferB, SizeB);

      int IniB = Search(BufferB, "<body>", SizeB);
      if(IniB!=-1){
        int FinB = Search(BufferB+IniB, "</body>", SizeB)+IniB;
        if(FinB!=-1){
          int IniA = Search(BufferA, "<body>", SizeA);
          if(IniA!=-1){
            int FinA = Search(BufferA+IniA, "</body>", SizeA)+IniA;
            if(FinA!=-1){
              _llseek((int)hFileA, FILE_BEGIN, 0);
              SetEndOfFile(hFileA);
              _lwrite((int)hFileA, BufferA, IniA);
              _lwrite((int)hFileA, BufferB + IniB, FinB-IniB);
              _lwrite((int)hFileA, BufferA + FinA, SizeA-FinA);
            }
          }
        }
      }
      delete [] BufferA;
      delete [] BufferB;
    }
    CloseHandle(hFileB);
  }
  CloseHandle(hFileA);
}


Saludos.

aguml 04-07-2014 10:24:25

guau!!! He visto que te has liado y has cambiado algun nombre pero increible amigo. ¡Muchas gracias! Ya lo probaré en cuanto pueda.

escafandra 04-07-2014 11:22:01

Cita:

Empezado por aguml (Mensaje 478680)
...has cambiado algun nombre....

Ahora que lo dices y mirando el mensaje, si. Las prisas y el copy/paste de líneas de código similares...:( Donde dice
Código:

DWORD SizeB = GetFileSize(hFileA, 0);
Debe decir:
Código:

DWORD SizeB = GetFileSize(hFileB, 0);
Por lo demás, usa las cadenas que tengas que buscar tengan el m¡nombre que tengan. En principio es sensible a mayúsculas pero puedes modificar fácilmente la función de búsqueda con un simple toupper.

Saludos.

aguml 04-07-2014 12:16:34

nooo si es fantastico que sea case sensitive. Ahora tengo otra duda, igual que insrto texto, tambien tengo que sustituir texto que se encuentre entre etiquetas, por ejemplo, entre <title> y </title> sustituir lo que haya por lo que hay en el otro archivo entre esas mismas etiquetas. Se me ocurre hacer una copia de la funcion insert y modificarla un poco para que haga eso. Ahora bien, asi tendria que abrir y cerrar el archivo mas de una vez. No se si eso ralentizaria el proceso. ¿Que opinas?

escafandra 04-07-2014 12:41:24

En realidad la función sustituye texto, no inserta. El nombre de la función no es muy afortunado...

Saludos.

aguml 04-07-2014 15:38:51

no me entendiste, la tuya si insertaria pero la idea es usar una copia de la tuya algo modificada a la que llamariamos Replace o algo asi y poder usar tanto Insert como Replace por separado.

escafandra 04-07-2014 17:29:27

Cita:

Empezado por aguml (Mensaje 478694)
no me entendiste, la tuya si insertaria pero la idea es usar una copia de la tuya algo modificada a la que llamariamos Replace o algo asi y poder usar tanto Insert como Replace por separado.

Si te entendí. Lo que digo es que mi función hace un "Replace" del texto entre las etiquetas dadas. Por su puesto que puedes insertar, en lugar de reemplazar modificando la función. Los archivos de texto no son muy grandes por lo que en abrir un archivo, leerlo y reescribirlo no vas a tardar mucho tiempo, asi que no debe importarte el detalle de varias aperturas y lecturas.

Saludos.

aguml 04-07-2014 18:39:57

ok, tienes razon, no lo vi que buscabas el FinA jejeje. Entonces se me ocurre creae una lista doble donde poner las etiquetas iniciales y finales a reemplazar su contenido y que con un for(int i=1;i<=lista->count;i++) pues va buscando todas las etiquetas y las sustituye.

escafandra 04-07-2014 18:49:20

Como la función trabaja con un buffer que salva en el archivo la modificación, un bucle en la función no te sirve. Ten en cuenta que actúa sobre la primera etiqueta que coincida.

Puedes, en lugar de guardar directamente en disco, crear un buffer secundario que vas llenando con los datos del archivoA modificado y guardarlo entero al finalizar. Ten en cuenta que si vas a insertar más tamaño que el archivoA original, deberás reservar más memoria que el tamaño del archivo. Yo no o hago porque solo leo, el guardar lo hago en disco directamente. Al funcionar con un buffer temporal, puedes usar un bucle solo debes controlar el puntero del BufferA
y BufferB adecuadamente para que la siguiente búsqueda sea la de la siguiente etiqueta con el mismo nombre (es una suma de puntero del buffer + el último índice final encontrado).


Saludos.

aguml 04-07-2014 19:11:39

bueno yo decia poner la llamada a la funcion dentro del bucle por eso dcia lode abrir y cerrar archivos varias veces. Otra opcion podria ser crear una clase donde se podrian poner como metodos AbrirA, AbrirB, Reemplazar, CerrarA, y CerrarB y antes del for llamo a AbrirA y AbrirB y si todo fue bien entro en el bucle donde reemplazo todas y al salir del bucle llamo a CerrarA y CerrarB. Con eso evito abrir los archivos varias veces. Ademas el archivo de salida no seria ni A, ni B, seria uno nuevo ya que A seria una plantilla, B seria el contenido a insertar y C seria el archivo de salida. Creo que no seria complicado modificar tu código para que funcione así. Supongo que tendria que tener una variable la cual controle si ya he creado a C para que para la segunda pasada y demas pues use C en lugar de A. Esa es la idea que se me ocurrio.

escafandra 04-07-2014 19:16:52

El problema de varias entradas en la función es que si tienes barias etiquetas del mismo nombre pero en otras partes del archivo, sólo actuará en la primera, por eso te decía lo de un buffer intermedio y hacerlo todo de un tirón.

La versión de Builder que uso es muy antigua. En el caso de que tus archivos sean xml quizás te interese un componente que trabaje con ellos y te facilite la tarea en alto nivel. Yo soy amante del bajo nivel y del hágalo usted mismo...


Saludos.

escafandra 04-07-2014 19:43:39

Otra posibilidad para usar mi función en un bucle es modificar la función Search añadiendo un parámetro que indique la concurrencia que buscas (la 1ª, 2ª, 3ª...) con lo que esa función queda mucho mejor :D


Saludos.

aguml 04-07-2014 21:12:34

mmmm interesante. La verdad es que no me preocupa que haya varias entradas con la misma etiqueta ya que es una utilidad muy especifica que solo va a buscar a body y a title y esas no se pueden poner por duplicado ademas de que tanto el template como los datos a insertar los crearé yo a mano y la idea de esta herramienta es porque serán dos templates diferentes y rellenar el doble de htm a mano pues por eso es la idea de crear dos templates diferentes donde tanto el title como el contenido de body son los mismos y podria crear los contenidos en simples txt y luego aplicarlo a todos los templates que quiera. No se si me explico. Lo de la concurrencia ni idea de como hacerlo aunque en esta herramienta no lo necesito. Con estas funciones y un bucle con FindFirstFile y FindNextFile en la que vaya viendo todos los archivos del directorio y metiendo sus nombres en un tstringlist para luego aplicar tus funciones con todos ya tengo lo que deseaba. Cuando ya lo tenga codeado y probado ya te comento.

escafandra 04-07-2014 21:21:56

Bueno, ahora que he tenido un ratico con el PC te muestro la modificación con concurrencias:

Código:

int Search(char *Buffer, char *S, int SizeBuffer, int Concurrence = 0)
{
  int Con = 0;
  for(int n= 0; n<SizeBuffer-lstrlen(S); n++){
    if(Buffer[n] == *S){
      int i=1;
      for(; S[i]; i++)
        if(Buffer[n+i]!=S[i])  break;
      if(i==lstrlen(S)) if(Con++ == Concurrence) return n;
    }
  }
  return -1;
}

void Replace(char *FileNameA, char *FileNameB, int Concurrence = 0)
{
  HANDLE hFileA = CreateFile(FileNameA, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0);
  if(hFileA!=(HANDLE)(-1)){
    HANDLE hFileB = CreateFile(FileNameB, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0);
    if(hFileB!=(HANDLE)(-1)){

      DWORD SizeA = GetFileSize(hFileA, 0);
      DWORD SizeB = GetFileSize(hFileB, 0);

      char* BufferA = new char[SizeA+SizeB];
      char* BufferB = new char[SizeB];
      _lread((int)hFileA, BufferA, SizeA);
      _lread((int)hFileB, BufferB, SizeB);

      int IniB = Search(BufferB, "<body>", SizeB, Concurrence);
      if(IniB!=-1){
        int FinB = Search(BufferB+IniB, "</body>", SizeB);
        if(FinB!=-1){
          FinB += IniB;
          int IniA = Search(BufferA, "<body>", SizeA, Concurrence);
          if(IniA!=-1){
            int FinA = Search(BufferA+IniA, "</body>", SizeA);
            if(FinA!=-1){
              FinA += IniA;
              _llseek((int)hFileA, FILE_BEGIN, 0);
              SetEndOfFile(hFileA);
              _lwrite((int)hFileA, BufferA, IniA);
              _lwrite((int)hFileA, BufferB + IniB, FinB-IniB);
              _lwrite((int)hFileA, BufferA + FinA, SizeA-FinA);
            }
          }
        }
      }
      delete [] BufferA;
      delete [] BufferB;
    }
    CloseHandle(hFileB);
  }
  CloseHandle(hFileA);
}

La primera concurrencia es la 0.
Aprovecho mara reservar más memoria para el BufferA para asegurar que no se queda corto si FileB es más grande.

Saludos.

aguml 04-07-2014 22:21:46

hay algunas cosas que no entiendo, en la funcion Search tienes esto: if(i==lstrlen(S)) if(Con++ == Concurrence) return n;
 }
, la parte de Con++ ¿Primero compara y luego incrementa? Otra cosa, veo que Concurrencia en la declaracion haces = 0 ¿Es 0 si no introduces ningun valor en ese argumento pero si introduces otro valor ya no es 0? Es que nunca vi que declararan asi una funcion. ¿La funcion search recive el numero de veces a buscarlo y cuando encuentra esa cantidad de concurrencias retorla la posicion de la ultima? O sea que a replace le indico que me reemplace lo que esté dentro de la etiqueta body tercera digamoslo asi, y en search buscaria la tercera y sa seria la posicion que retornaria ¿No?

escafandra 04-07-2014 23:11:45

Cita:

Empezado por aguml (Mensaje 478727)
hay algunas cosas que no entiendo, en la funcion Search tienes esto: if(i==lstrlen(S)) if(Con++ == Concurrence) return n;
 }
, la parte de Con++ ¿Primero compara y luego incrementa?

Exacto. Sin embargo esto:
Código:

if(++Con == Concurrence)
Primero incrementa y luego compara. Se trata de las contracciones de C. Puedes no usarlo escribiendo un poco más de código.

Cita:

Empezado por aguml (Mensaje 478727)
Otra cosa, veo que Concurrencia en la declaracion haces = 0

Es el valor por defecto. Si no lo pones en la llamada toma ese valor. En delphi también existe.

Cita:

Empezado por aguml (Mensaje 478727)
¿La funcion search recive el numero de veces a buscarlo y cuando encuentra esa cantidad de concurrencias retorla la posicion de la ultima? O sea que a replace le indico que me reemplace lo que esté dentro de la etiqueta body tercera digamoslo asi, y en search buscaria la tercera y sa seria la posicion que retornaria ¿No?

Busca el número de concurrencia que quieres, teniendo en cuenta que la primera es la 0. Claro que si "amputas" el buffer en su inicio, como hago al buscar </body> entonces no debes usarlo puesto que el primero que encuentre será el que buscas. Mira el código cuando busco los puntos finales.


Saludos.

aguml 04-07-2014 23:44:15

ok amigo, mil gracias, ya te cuento que tal me fue.


La franja horaria es GMT +2. Ahora son las 00:29:29.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi