modificare i contenuti esistenti del file in c

int main() { FILE *ft; char ch; ft=fopen("abc.txt","r+"); if(ft==NULL) { printf("can not open target file\n"); exit(1); } while(1) { ch=fgetc(ft); if(ch==EOF) { printf("done"); break; } if(ch=='i') { fputc('a',ft); } } fclose(ft); return 0; } 

Come si può vedere che voglio modificare abc.txt in modo tale che i sia sostituito da a in esso.
Il programma funziona bene ma quando apro esternamente abc.txt , sembra che non sia stato modificato.
Qualche ansible ragione per quello?

Perché in questo caso il carattere dopo i non è sostituito da a , come suggeriscono le risposte?

Analisi

Ci sono più problemi:

  1. fgetc() restituisce un int , non un char ; deve restituire ogni valore char valido più un valore separato, EOF. Come scritto, non è ansible rilevare in modo affidabile EOF. Se char è un tipo senza segno, non troverai mai EOF; se char è un tipo firmato, si identificheranno erroneamente alcuni caratteri validi (spesso ÿ, y-umlaut, U + 00FF, LATIN SMALL LETTER Y WITH DIAERESIS) come EOF.

  2. Se si passa da input a output su un file aperto per la modalità di aggiornamento, è necessario utilizzare un’operazione di posizionamento dei file ( fseek() , rewind() , nominalmente fsetpos() ) tra lettura e scrittura; e devi usare un’operazione di posizionamento o fflush() tra scrittura e lettura.

  3. È una buona idea chiudere ciò che apri (ora fissato nel codice).

  4. Se le tue scritture funzionavano, sovrascrivi il personaggio dopo l’ i con a .

Sintesi

Queste modifiche portano a:

 #include  #include  int main(void) { FILE *ft; char const *name = "abc.txt"; int ch; ft = fopen(name, "r+"); if (ft == NULL) { fprintf(stderr, "cannot open target file %s\n", name); exit(1); } while ((ch = fgetc(ft)) != EOF) { if (ch == 'i') { fseek(ft, -1, SEEK_CUR); fputc('a',ft); fseek(ft, 0, SEEK_CUR); } } fclose(ft); return 0; } 

C’è spazio per ulteriori verifiche degli errori.

Esegesi

L’input seguito dall’output richiede la ricerca

Il fseek(ft, 0, SEEK_CUR); la dichiarazione è richiesta dallo standard C.

ISO / IEC 9899: 2011 §7.21.5.3 La funzione fopen

¶7 Quando un file viene aperto con la modalità di aggiornamento (‘+’ come secondo o terzo carattere nell’elenco precedente dei valori degli argomenti della modalità), sia l’input che l’output possono essere eseguiti sullo stream associato. Tuttavia, l’uscita non deve essere seguita direttamente da un input senza una chiamata fflush funzione fflush o ad una funzione di posizionamento del file ( fseek , fsetpos o rewind ) e l’input non deve essere seguito direttamente dall’output senza una chiamata fsetpos al posizionamento del file funzione, a meno che l’operazione di immissione non incontri la fine del file. Aprire (o creare) un file di testo con la modalità di aggiornamento può invece aprire (o creare) un stream binario in alcune implementazioni.

(Enfasi aggiunta.)

fgetc() restituisce un int

Citazioni da ISO / IEC 9899: 2011, l’attuale standard C.

§7.21 Input / output

§7.21.1 Introduzione

EOF che si espande a un’espressione costante intera, con tipo int e un valore negativo, che viene restituito da diverse funzioni per indicare la fine del file, ovvero, non più input da un stream;

§7.21.7.1 La funzione fgetc

int fgetc(FILE *stream);

¶2 Se l’indicatore di fine file per il stream di input puntato da stream non è impostato e un carattere successivo è presente, la funzione fgetc ottiene quel carattere come un unsigned char convertito in un int e fa avanzare l’indicatore di posizione file associato per lo stream (se definito).

ritorna

¶3 Se l’indicatore di fine file per il stream è impostato, o se il stream è alla fine del file, viene impostato l’indicatore di fine del file per lo streaming e la funzione fgetc restituisce EOF. Altrimenti, la funzione fgetc restituisce il carattere successivo dal stream di input a cui punta il stream. Se si verifica un errore di lettura, viene impostato l’indicatore di errore per il stream e la funzione fgetc restituisce EOF. 289)

289) Un end-of-file e un errore di lettura possono essere distinti mediante l’uso delle funzioni feof e ferror .

Quindi, EOF è un numero intero negativo (per convenzione è -1, ma lo standard non richiede questo). La funzione fgetc() restituisce EOF o il valore del carattere come un unsigned char (nell’intervallo 0..UCHAR_MAX, in genere 0..255).

§6.2.5 Tipi

¶3 Un object dichiarato come tipo char è abbastanza grande da memorizzare qualsiasi membro del set di caratteri di esecuzione di base. Se un membro del set di caratteri di esecuzione di base è memorizzato in un object char , il suo valore è garantito non negativo. Se qualsiasi altro carattere è memorizzato in un object char , il valore risultante è definito dall’implementazione ma deve essere compreso nell’intervallo di valori che possono essere rappresentati in quel tipo.

¶5 Un object dichiarato come tipo signed char occupa la stessa quantità di memoria di un object char “plain”.

§6 Per ciascuno dei tipi di interi con unsigned , esiste un corrispondente (ma diverso) tipo di intero senza segno (designato con la parola chiave unsigned ) che utilizza la stessa quantità di memoria (comprese le informazioni sul segno) e ha gli stessi requisiti di allineamento.

§15 I tre tipi char , signed char e unsigned char sono chiamati collettivamente i tipi di caratteri. L’implementazione deve definire char per avere lo stesso intervallo, rappresentazione e comportamento come signed char o unsigned char . 45)

45) CHAR_MIN , definito in , avrà uno dei valori 0 o SCHAR_MIN , e questo può essere usato per distinguere le due opzioni. Indipendentemente dalla scelta effettuata, char è un tipo separato dagli altri due e non è compatibile con nessuno dei due.

Ciò giustifica la mia affermazione che il char normale può essere un tipo firmato o non firmato.

Considerare ora:

 char c = fgetc(fp); if (c == EOF) … 

Supponiamo che fgetc() restituisca EOF, e plain char sia un tipo unsigned (8-bit), e EOF è -1 . L’assegnazione inserisce il valore 0xFF in c , che è un numero intero positivo. Quando viene eseguito il confronto, c viene promosso a un int (e quindi al valore 255) e 255 non è negativo, quindi il confronto fallisce.

Viceversa, supponiamo che plain char sia un tipo con char (8 bit) e il set di caratteri sia ISO 8859-15. Se fgetc() restituisce ÿ, il valore assegnato sarà il modello di bit 0b11111111, che è uguale a -1 , quindi nel confronto, c verrà convertito in -1 e il confronto c == EOF restituirà true anche se un è stato letto un carattere valido.

È ansible modificare i dettagli, ma l’argomento di base rimane valido mentre sizeof(char) < sizeof(int) . Ci sono i chip DSP dove non si applica; devi ripensare alle regole. Anche così, il punto fondamentale rimane; fgetc() restituisce un int , non un char .

Se i tuoi dati sono veramente ASCII (dati a 7 bit), tutti i caratteri sono compresi nell'intervallo 0..127 e non ti imbatterai nell'interpretazione errata del problema ÿ. Tuttavia, se il tuo tipo di char non è firmato, hai ancora il problema "imansible rilevare EOF", quindi il tuo programma verrà eseguito per un lungo periodo di tempo. Se devi considerare la portabilità, ne terrà conto. Questi sono i problemi di livello professionale che devi gestire come programmatore C. Puoi tranquillamente scegliere programmi che funzionano sul tuo sistema per i tuoi dati in modo relativamente semplice e senza tenere conto di tutte queste sfumature. Ma il tuo programma non funzionerà sui sistemi di altre persone.

Non stai cambiando la “i” in abc.txt , stai cambiando il carattere successivo dopo “i”. Prova a mettere fseek(ft, -1, SEEK_CUR); prima del tuo fputc('a', ft); .

Dopo aver letto un carattere ‘i’, l’indicatore di posizione del file di ft sarà il carattere dopo questo ‘i’, e quando si scrive un carattere con fputc() , questo carattere sarà scritto nella posizione corrente del file, cioè il carattere dopo io’. Vedi fseek(3) per ulteriori dettagli.

Dopo aver letto ‘i’ è necessario “tornare indietro” per scrivere nella posizione corretta.

 if(ch=='i') { fseek(ft, -1, SEEK_CUR); fputc('a',ft); }