0

Estoy intentando copiar el contenido de un fichero en un vector de strings en una función que recibe como parámetros los nombres de los ficheros.

#include <fstream>
#include <iostream>
#include <string>
#define DIM 1000000
using namespace std;

int CopiaFichero(string nfich, string palabras[]);
void RellenaFichero(string nfichero, string palabras[], int nt);



int main (void)

{
    string norigen, ndestino;
    string palabras[DIM];
    int nt;


    cout << " Introduzca el nombre del archivo origen: ";
    getline(cin, norigen);
    cout<< endl;


    cout<< " Introduzca el nombre del archivo destino: ";
    getline(cin, ndestino);

    nt=CopiaFichero(norigen, palabras);
    RellenaFichero(ndestino, palabras, nt);

    system("pause");

    return 0;

  }


    int CopiaFichero(string nfich, string palabras[])

    {
    ifstream fichero;
    int i;

    fichero.open(nfich.c_str());

         i=0;
        while(fichero.eof())

        {
            getline(fichero, palabras[i]);
            i++;
        }

        fichero.close();

    return i;
    }


    void RellenaFichero(string nfichero, string palabras[], int nt)

    {
    ofstream fichero;
    int j;

    fichero.open(nfichero.c_str());

    for(j=0; j<=nt; j++)

    {
        fichero << palabras[j]<<endl;
    }


    fichero.close();

    return;
    }

He obviado la comprobación fichero.is_open por sencillez. Luego paso por referencia el vector a otra función para que rellene un nuevo fichero pero al compilar me da error. Les paso el código por si alguien pudiera echarme una mano.

La cosa es que al ejectutarlo ni siquiera aparece en pantalla lo de pedir el nombre de los ficheros, es decir falla directamente sin ejecutar si quiera la función principal. Así que algo habrá de estar mal en el código.

Benito-B
  • 3,831
  • 3
  • 11
  • 28
  • 1
    *falla directamente sin ejecutar si quiera la función principal.* -> Cómo falla? Explota? Se incendia el PC? Llama a la policía? Te apunta con una pistola? Se cuelga el pc? Simplemente no hace nada? En serio, no somos adivinos, explícanos como falla por favor :) – Benito-B Dec 27 '21 at 00:19
  • Una primera observación rápida es que la función `void RellenaFichero()` no debe de retornar nada. No arreglará tu problema pero puedes quitar el return perfectamente. – user3733164 Dec 27 '21 at 08:45

1 Answers1

1

Problema.

Sospecho que el problema es pedir un millón de std::string en memoria automática:

#define DIM 1000000

// ...

    string palabras[DIM];

Las variables que declaras de manera automática (sin utilizar el operador new) van a parar a la pila. La pila tiene un espacio limitado que cambia según la implementación y la configuración del compilador pero suele ser de unos pocos kilobytes.

En cambio, si un std::string (vacío) ocupa unos 32 bytes y pides un millón de ellos, estarás pidiendo 32 megabytes (un valor muy por encima del límite de la pila) lo cuál provoca un error en tiempo de ejecución dando lugar al comportamiento que describes:

al ejectutarlo ni siquiera aparece en pantalla lo de pedir el nombre de los ficheros, es decir falla directamente sin ejecutar si quiera la función principal.

Ya que al momento de entrar en main debe hacer la solicitud de memoria de las variables que contiene y la tercera de esas variables desborda la pila.

Solución.

En lugar de usar memoria automática, usa un contenedor (que internamente usa memoria dinámica), puedes consultar aquí qué contenedor se ajusta a tus necesidades, yo te pondré un ejemplo con std::list y modernizaré un poco tu código:

Propuesta:

Puedes ver el código funcionando en Try it online!.

#include <fstream>
#include <iostream>
#include <string>
#include <list>

// No usaremos memoria automática, así que esto es innecesario
// vvvv
// #define DIM 1000000

using namespace std;
using list_string = list<string>;

/*
el argumento 'nfich' es un string pasado por referencia constante porque:
    - Referencia: Evitamos copiar su contenido.
    - Constante: No pretendemos modificar su contenido.
el argumento 'palabras' es un lista_string pasado por referencia porque:
    - Referencia: Pretendemos modificar su contenido dentro de la función.
No necesitamos que devuelva los elementos leidos porque la lista ya nos informa de su tamaño.
*/
void CopiaFichero(const string &nfich, list_string &palabras);

/*
el argumento 'nfichero' es un string pasado por referencia constante porque:
    - Referencia: Evitamos copiar su contenido.
    - Constante: No pretendemos modificar su contenido.
el argumento 'palabras' es un lista_string pasado por referencia constante porque:
    - Referencia: Evitamos copiar su contenido.
    - Constante: No pretendemos modificar su contenido.
No pasamos una variable con la cantidad de elementos porque la lista ya nos informa de ello.
*/
void RellenaFichero(const string &nfichero, const list_string &palabras);


// En C++ las funciones que no reciben parámetros se definen con paréntesis vacíos
int main ()
{
    string norigen, ndestino;
    list_string palabras;

    cout << " Introduzca el nombre del archivo origen: ";
    getline(cin, norigen);
    cout << endl;

    cout << " Introduzca el nombre del archivo destino: ";
    getline(cin, ndestino);

    CopiaFichero(norigen, palabras);
    RellenaFichero(ndestino, palabras);

    return 0;
}


void CopiaFichero(const string &nfich, list_string &palabras)
{
    /* No hace falta llamar a .open(), pasando al constructor
    un nombre de archivo lo abre al construirse. */
    ifstream fichero{nfich};

    // Un bucle sobre .eof() es incorrecto (ver notas al final)
        // while(fichero.eof())

    // Cuando 'fichero' equivalga a 'false' se habrá acabado la lectura.
    for (string palabra; fichero; getline(fichero, palabra))
        palabras.push_back(palabra);

    /* No hace falta llamar a .close(), cuando se acaba la función
    se llama automáticamente. */
}

void RellenaFichero(const string &nfichero, const list_string &palabras)
{
    /* No hace falta llamar a .open(), pasando al constructor
    un nombre de archivo lo abre al construirse. */
    ofstream fichero{nfichero};

    /* Puedes usar un for de rango, sin necesidad de usar una
    variable de contador.
    */
    for (const auto &palabra : palabras)
        fichero << palabra << endl;

    /* No hace falta llamar a .close(), cuando se acaba la función
    se llama automáticamente. */

    // No hace falta llamar 'return' para acabar una función.
    // return;
}

No uses EOF en un while.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82