Risolto [Problema] Leggere file di testo

enk17

Utente Bronze
23 Ottobre 2021
69
17
8
37
Salve starei scrivendo un programma che a seconda della parola che inserisci ti dice se c'è nel testo, il problema che ho è che non riesco a far leggere correttamente il file ovvero, se scrivo nel file ogni parola e vado daccapo funziona ma con la punteggiatura va in difficoltà, vorrei capire come dovrei risolvere questo problema.


C++:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;

int main()
{

string ricerca;
bool verifica=false;
cout<<"inserisci la parola che vuoi cercare : ";

getline(cin,ricerca);

// filestream variabili
    fstream file;
    string word, t, q, filename;
 
    //dichiariamo filename che corrisponde al file che vogliamo aprire
    filename = "file.txt";
 
    //apertura del file
    file.open(filename.c_str());
 
 
    //dichiariamo un ciclo for per leggere tutte le parole
    /*for(int x=0 ; file >> word; x++)
    {
    sono a conoscenza che questo for non "serve a niente " */
    //confronto con l'input dell'utente
    if(word==ricerca)
    {
verifica=true;
    }
    }
 
    //stampa dei risultati
    if(verifica==true)
    {
    cout<<" la parola " << ricerca << " è presente nel testo!";
    }else{
    cout<<" la parola " << ricerca << " non è presente nel testo!";
    }
    return 0;
}
 
Il getline per leggere la parola non ti serve perché una parola non contiene spazi (e.g., "hello world" sono due parole). Con l'operatore >> leggi un file parola per parola, dove ogni parola dove le parole sono delimitate da spazi. Il problema è che di default non ti elimina i simboli(e.g., in "hello, world" la prima parola è "hello," con la virgola). Ti posto direttamente la soluzione:
C++:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

// Leggi un file e restituisci una stringa il suo intero contenuto.
string file_content(string filename) {
  ifstream file(filename);
  stringstream buffer;
  buffer << file.rdbuf();
  return buffer.str();
}

// Tutto ciò che non è alfabetico né uno spazio è "bad" (da scartare).
bool badchar(char c) { return !isalpha(c) && !isspace(c); }

// Controlla se pattern è una parola (stringa separata da spazi) in text.
bool has_word(string pattern, string text) {
  istringstream stream(text); 
  for (string word; stream >> word;) // per ogni parola nel testo
    if (word == pattern) return true; 
  return false;
}

int main() {
  string pattern;
  cout << "Inserisci la parola che vuoi cercare: ";
  cin >> pattern;

  // Leggi tutto il file (non solo la prima riga) e salvalo in text.
  string text = file_content("file.txt");

  // Sostituisci tutti i badchar con uno spazio.
  replace_if(text.begin(), text.end(), badchar, ' ');

  // Controlla se la parola è contenuta nel testo.
  cout << "La parola \"" << pattern << "\" ";
  if (!has_word(pattern, text)) cout << "non "; // se non c'è, scrivi "non"
  cout << "è presente nel testo!\n";

  return 0;
}

Se hai domande, chiedi pure.
 
Ultima modifica:
stringstream buffer; buffer << file.rdbuf(); return buffer.str();
non mi è chiaro questo, in questo passaggio in poche parole stiamo mettendo il contenuto del file nella variabile buffer?

Provando ad implentare la prima parte del codice restituisce questo errore
Error] conflicting declaration 'std::ifstream file
 
Quando senti parlare di stream vuol dire che hai a che fare con una sequenza che consumi sequenzialmente. In particolare, lo stringstream è una sequenza di caratteri. La differenza principale tra stringstream e string è che lo devi consumare sequenzialmente: prima di leggere l'n-esimo carattere devi leggere i primi n-1 caratteri e tipicamente non torni mai indietro perché quello che hai letto è stato consumato. I file tipicamente vengono letti come stream di bytes e
C++:
stringstream buffer;    // crea uno stringstream                         
buffer << file.rdbuf(); // passa allo stringstream il buffer del file
crea uno stringstream e lo inizializza per far si che quando leggi qualcosa da quello stream vai a prendere (a mo' dì stream, quindi a spezzettato sequenzialmente) il contenuto del file. Dopo queste due linee non hai ancora letto il file, ma hai uno stream che ti permette di farlo. La lettura del file avviene con
C++:
return buffer.str();
che consuma tutto quello che c'è nello stringstream e lo trasforma in una stringa.

Tutta la funzione file_content, come dice il nome, serve per prendere tutto il contenuto del file e metterlo in una stringa. Al termine della funzione il file viene chiuso automaticamente e lo puoi anche cancellare: tutto quello che ti serve è in memoria, quindi non fai più accessi al disco.

Provando ad implentare la prima parte del codice restituisce questo errore
Error] conflicting declaration 'std::ifstream file
Il codice che ho postato io compila correttamente. Probabilmente hai commesso qualche errore quando hai copiato e modificato per renderlo tuo. Puoi postare tutto il codice che hai scritto?
 
Ultima modifica:
Il codice che ho postato io compila correttamente. Probabilmente hai commesso qualche errore quando hai copiato e modificato per renderlo tuo. Puoi postare tutto il codice che hai scritto?
C++:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

string file_content();
bool badchar();
bool has_word();


int main()
{

string pattern;
cout<<"inserisci la parola che stai cercando : ";
cin>>pattern;

//lettura del file
string text = file_content("file.txt");

//sostituiamo tutti i badchar con uno spazio
replace_if(text.begin(), text.end(), badchar, ' ');
  
//stampa dei risultati
if(!has_word(pattern,text))
    {
    cout<<"la parola "<<pattern<<" non  è presente nel testo!";
    }else{
        cout<<"la parola "<< pattern << "è presente nel testo!";
    }
return 0;
}


string file_content(string filename)
{
    // filestream variabili
    fstream file;
    string filename;
 
    //dichiariamo filename che corrisponde al file che vogliamo aprire
    filename = "file.txt";
 
    //apertura del file
    file.open(filename.c_str());
    
    stringstream buffer;
      buffer << file.rdbuf();
return buffer.str();
    
}

// Controlla se pattern è una parola (stringa separata da spazi) in text.
bool has_word(string pattern, string text)
 {
  istringstream stream(text);
 
  for (string word; stream >> word;)  // per ogni parola nel testo
    if (word == pattern)
    {
    return true; 
    }else{
    return false;}
}

// Tutto ciò che non è alfabetico né uno spazio è "bad" (da scartare).
bool badchar(char c)
{
return !isalpha(c) && !isspace(c);
}

ora mi restituisce questi errori:

21 38 [Error] too many arguments to function 'std::string file_content()' 8 8 [Note] declared here (sembra strano perchè credo di averlo dichiarato bene) 41 12 [Error] declaration of 'std::string filename' shadows a parameter


Questi sono i miei primi lavori con le funzioni e queste librerie quindi scusami se sono errori basilari
 
I prototipi di funzione devono devono essere esattamente come le funzioni che vanno a dichiarare:
C++:
string file_content(string filename);
bool badchar(char c);
bool has_word(string pattern, string text);
al massimo puoi togliere il nome dei parametri, ma non il loro tipo.

La variabile filename in file_content è un parametro della funzione: è una variabile che viene definita nel momento in cui chiami la funzione e non all'interno della funzione stessa. O fai una cosa del genere:
C++:
string file_content(string filename) { // filename è un parametro
  // filestream variabili
  fstream file;

  // apertura del file
  file.open(filename.c_str());

  stringstream buffer;
  buffer << file.rdbuf();
  return buffer.str();
}
oppure fai una cosa del genere:
C++:
string file_content() { // nessun parametro
  // filestream variabili
  fstream file;
  string filename;

  // dichiariamo filename che corrisponde al file che vogliamo aprire
  filename = "file.txt";

  // apertura del file
  file.open(filename.c_str());

  stringstream buffer;
  buffer << file.rdbuf();
  return buffer.str();
}
ricordandoti che nel secondo caso dovrai scrivere string text = file_content();.

L'errore più grosso è in has_word, perché l'else proprio non ci vuole:
C++:
bool has_word(string pattern, string text) {
  istringstream stream(text);

  for (string word; stream >> word;) {
    if (word == pattern) {
      return true;
    } // else: continua normalmente con il for 
  }

  // se tutto il for termina senza mai restiture un valore, restituisci false.
  return false;
}
 
  • Mi piace
Reazioni: enk17
I prototipi di funzione devono devono essere esattamente come le funzioni che vanno a dichiarare:
C++:
string file_content(string filename);
bool badchar(char c);
bool has_word(string pattern, string text);
al massimo puoi togliere il nome dei parametri, ma non il loro tipo.

La variabile filename in file_content è un parametro della funzione: è una variabile che viene definita nel momento in cui chiami la funzione e non all'interno della funzione stessa. O fai una cosa del genere:
C++:
string file_content(string filename) { // filename è un parametro
  // filestream variabili
  fstream file;

  // apertura del file
  file.open(filename.c_str());

  stringstream buffer;
  buffer << file.rdbuf();
  return buffer.str();
}
oppure fai una cosa del genere:
C++:
string file_content() { // nessun parametro
  // filestream variabili
  fstream file;
  string filename;

  // dichiariamo filename che corrisponde al file che vogliamo aprire
  filename = "file.txt";

  // apertura del file
  file.open(filename.c_str());

  stringstream buffer;
  buffer << file.rdbuf();
  return buffer.str();
}
ricordandoti che nel secondo caso dovrai scrivere string text = file_content();.

L'errore più grosso è in has_word, perché l'else proprio non ci vuole:
C++:
bool has_word(string pattern, string text) {
  istringstream stream(text);

  for (string word; stream >> word;) {
    if (word == pattern) {
      return true;
    } // else: continua normalmente con il for
  }

  // se tutto il for termina senza mai restiture un valore, restituisci false.
  return false;
}
Ok, sono riuscito ad implementarlo però non mi è chiaro ancora l'ultima cosa, il confronto con pattern con tutto il testo avviene in has_word?
 
Ok, sono riuscito ad implementarlo però non mi è chiaro ancora l'ultima cosa, il confronto con pattern con tutto il testo avviene in has_word?
Esatto. Se la divisione in funzione ti confonde, si può fare anche senza. L'idea comunque è molto semplice: scorri tutte le parole nel testo e appena trovi una parola uguale al pattern, restituisci true; se non trovi nessuna parola uguale al pattern (i.e., se il ciclo for arriva al termine) allora restituisci false. La tua modifica (l'else all'interno del for) non andava bene perché stava a significare: se la parola attuale è uguale al pattern restituisci true, altrimenti restituisci false.

Ti basta trovare una condizione vera per restituire vero, ma devi verificare che non ce ne sia nessuna vera prima di restituire falso. Tu stavi provando a farlo con una flag, che andava comunque bene, io ti ho proposto la funzione come metodo alternativo.
 
Esatto. Se la divisione in funzione ti confonde, si può fare anche senza. L'idea comunque è molto semplice: scorri tutte le parole nel testo e appena trovi una parola uguale al pattern, restituisci true; se non trovi nessuna parola uguale al pattern (i.e., se il ciclo for arriva al termine) allora restituisci false. La tua modifica (l'else all'interno del for) non andava bene perché stava a significare: se la parola attuale è uguale al pattern restituisci true, altrimenti restituisci false.

Ti basta trovare una condizione vera per restituire vero, ma devi verificare che non ce ne sia nessuna vera prima di restituire falso. Tu stavi provando a farlo con una flag, che andava comunque bene, io ti ho proposto la funzione come metodo alternativo.
quindi se vorrei aggiungere un semplice contatore che dice quante volte c'è quella parola nel testo dovrei un pò modificare l'if che confronta word con pattern?
 
quindi se vorrei aggiungere un semplice contatore che dice quante volte c'è quella parola nel testo dovrei un pò modificare l'if che confronta word con pattern?
Provaci, mal che vada posta il tuo tentativo e vediamo di sistemarlo. Comunque:
  • la funzione dovrebbe restituire un int invece di un bool;
  • invece di return true devi incrementare il contatore;
  • invece di return false devi restituire il valore del contatore.
 
Provaci, mal che vada posta il tuo tentativo e vediamo di sistemarlo. Comunque:
  • la funzione dovrebbe restituire un int invece di un bool;
  • invece di return true devi incrementare il contatore;
  • invece di return false devi restituire il valore del contatore.
Allora ho provato a modifcare has_word e credo che l'abbia modificato bene
C++:
int has_word(string pattern, string text, int contatore)
 {
  istringstream stream(text);
  contatore=0;
  for (string word; stream >> word;)  // per ogni parola nel testo
    {
        if(word == pattern)
        {
        contatore++;
        }  
    }
    return contatore;
}

Però restituisce errore nella parte finale del cout ovvero questa parte
C++:
//stampa dei risultati
if(!has_word(pattern,text,contatore))
    {
    cout<<"la parola "<<pattern<<" non  e presente nel testo!";
    }else{
    cout<<"la parola "<< pattern << " e' presente nel testo ed e' presente "<<contatore<<" volte! ";
    }
return 0;
}

Errore è questo
Error] 'contatore' was not declared in this scope
 
Allora ho provato a modifcare has_word e credo che l'abbia modificato bene
Benissimo. Mi raccomando, modifica anche il prototipo di funzione in cima al codice.

Però restituisce errore nella parte finale del cout ovvero questa parte
Lo devi chiamare in questo modo:
C++:
int occorrenze = has_word(text, pattern);
poi fai l'if-else in base al valore di occorrenze.
 
Lo devi chiamare in questo modo:
C++:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

//dichiariamo le funzioni
string file_content();
bool badchar(char c);
int has_word(string pattern, string text, int contatore);


int main()
{
string pattern;
cout<<"inserisci la parola che stai cercando : ";
cin>>pattern;

//lettura del file
string text = file_content();


//sostituiamo tutti i badchar con uno spazio
replace_if(text.begin(), text.end(), badchar, ' ');

int occorrenze = has_word(pattern,text);


//stampa dei risultati
if(!has_word(pattern,text))
    {
    cout<<"la parola "<<pattern<<" non  e presente nel testo!";
    }else{
    cout<<"la parola "<< pattern << " e' presente nel testo ed e' presente "<<occorrenze<<" volte! ";
    }
return 0;
}

mi restituiscono questi errori
[Error] too few arguments to function 'int has_word(std::string, std::string, int)'
[Error] 'contatore' was not declared in this scope
 
Okay, ho letto frettolosamente e ti ho detto che andava bene anche se non era proprio così. La funzione has_word non andava bene: occorrenze non deve essere un parametro (qualcosa che gli passi da fuori), ma dev'essere una normale variabile (qualcosa che si gestisce da sola, internamente).
C++:
int has_word(string pattern, string text) {
  int contatore = 0;
  istringstream stream(text);
  for (string word; stream >> word;) // per ogni parola nel testo
  {
    if (word == pattern) {
      contatore++;
    }
  }
  return contatore;
}
modifica il prototipo di conseguenza.

Dopodiché:
C++:
int occorrenze = has_word(pattern, text);
if (occorrenze == 0) {
  cout << "la parola " << pattern << " non  e presente nel testo!";
} else {
  cout << "la parola " << pattern << " e' presente nel testo ed e' presente " << occorrenze << " volte! ";
}
 
  • Mi piace
Reazioni: enk17
Ok ora va,grazie mille sei stato gentilissimo!!
C++:
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

//dichiariamo le funzioni
string file_content();
bool badchar(char c);
int has_word(string pattern, string text);


int main()
{
string pattern;
cout<<"inserisci la parola che stai cercando : ";
cin>>pattern;

//lettura del file
string text = file_content();


//sostituiamo tutti i badchar con uno spazio
replace_if(text.begin(), text.end(), badchar, ' ');

int conteggio = has_word(pattern, text);


//stampa dei risultati

if (conteggio == 0)
    {
      cout << "la parola " << pattern << " non  e presente nel testo!";
    } else {
      cout << "la parola " << pattern << " e' presente nel testo ed e' presente " << conteggio << " volte! ";
    }

return 0;
}

//questa funzione prende il contenuto del file
string file_content()
{
    // filestream variabili
    fstream file;
    string filename;
 
    //dichiariamo filename che corrisponde al file che vogliamo aprire
    filename = "file.txt";
 
    //apertura del file
    file.open(filename.c_str());
      
    // crea uno stringstream
    stringstream buffer; 
    
    // passa allo stringstream il buffer del file
    buffer << file.rdbuf();

    //lettura file
    return buffer.str();
      
}

//questa funzione controlla se pattern è una parola (stringa separata da spazi) in text.
int has_word(string pattern, string text)
 {
  istringstream stream(text);
  int contatore=0;
  for (string word; stream >> word;)  // per ogni parola nel testo
    {
        if(word == pattern)
        {
        contatore++;
        }   
    }
    return contatore;
}

// Tutto ciò che non è alfabetico né uno spazio è "bad" (da scartare).
bool badchar(char c)
{
return !isalpha(c) && !isspace(c);
}

Questo è il codice completo!!