Newsgroup: it.comp.lang.c
Titolo: Curiosità da newbie sul EOF

Salve, ho scritto un txt con un ciclo for che stampa i primi 256 caratteri ASCII, perché il notepad di XP mostra 256 caratteri e linux dà pagina bianca?


Intanto mostra il codice... senno' e' complicata...
 
-- 
By ZeD

 ASCII, perché il notepad di XP mostra 256 caratteri e linux dà pagina bianca?
 
Stai paragonando un programma con un kernel. Che intendi con "linux dà 
pagina bianca", che stampi da programma su stdout e lanciando il 
programma da terminale non hai uscite visibili? Che la schermata del 
terminale si cancella? In questo caso quale terminale stai usando? Stai 
usando un editor di testo analogo a Notepad ma che gira in linux e 
questo non ti mostra il contenuto del file che hai generato con il tuo 
programma? Il file è lo stesso che con Notepad visualizzi correttamente?
 
A naso è qualche carattere di controllo che ti crea problemi.

 ASCII, perché il notepad di XP mostra 256 caratteri e linux dà pagina bianca?
 
1- Cosa c'entra EOF?
2- stai usando isgraph() (di ctype.h) per controllare che sia un carattere
visualizzabile e non uno di controllo, vero? :)
 
Ciao!

 A naso è qualche carattere di controllo che ti crea problemi.
 
avrei voluto scrivere un file binario anziché ascii dunque cestino il codice e cambio quesito: scrivendo un file binario posso utilizzare qualunque combinazione a 8 bit o c'è qualche "carattere di controllo" da evitare per non creare problemi? i sistemi operativi per riconoscono la fine di un file binario cercando il carattere di controllo o dal numero totale di byte? grazie a tutti

 grazie a tutti
 
Hai un po' di confusione.
Cominciamo col dire che questa cosa del file binario/testuale è una cosa con
poco senso: un file è una sequenza di bytes, punto. (1)
Adesso, quando hai un byte, questo lo puoi interpretare un po' come ti pare:
se lo tratti come indice della tabella ASCII avrai in un uscita un
carattere. (2)
Alcuni di questi caratteri hanno associato un glifo (come lettere e numeri),
altri assumono un siginificato particolare per i vari sistemi di output
(terminali, stampanti, text-editor, etc.).
Se vuoi essere sicuro di mandare in output solo i caratteri "visualizzabili"
devi usare la funzione isgraph() o simili, che ritorna non zero se ritorna.
 
Ciao!
 
(1) Alcuni sistemi differenziavano la gestione a medio livello dei cosiddetti
"file di testo" per come venivano gestiti i caratteri di carriage return e 
line feed.
(2) La tabella ASCII a 8-bit, visto che oggi normalmente si usa la 7-bit o 
direttamente lo UTF-8.

 devi usare la funzione isgraph() o simili, che ritorna non zero se ritorna.
 
Non mi interessano i caratteri visualizzabili e l'impaginazione, vorrei scrivere un file di bit per otto e quattr'otto che dovrebbe contenere qualunque combinazione ASCII, la mia curiosità è capire appunto se ciò è possibile o se si incappa in problemi tipo l'eventualità di un eof anticipato o altro. Per questo mi chiedo come i sistemi operativi delimitino l'inizio e la fine di un file.


 
Gli OS non possono memorizzare una serie di bytes particolare per 
indicare la fine proprio perch� qualsiasi sequenza di bytes � valida
Memorizzano da qualche parte il numero di bytes  che compongono il file

 che  ritorna non zero se ritorna.
 
Ho molti problemi a capire il senso di questa frase. Cosa vuol 
dire "se ritorna"?
 
 

 Memorizzano da qualche parte il numero di bytes  che compongono il file
 
Non proprio. Cosa succede realmente dipende dal sistema operativo.
Tipicamente memorizzano la grandezza del file come dici tu ma 
possono usare molti metodi, compreso un indicatore particolare per 
indicare la fine del file. La tendenza attuale è usare strutture molto 
complesse per memorizzare i metadati e diventano possibili molti 
modi per determinare la fine di un file. Per contro sistemi vecchi 
non si pongono minimamente il problema; CP/M per esempio specifica
il numero di blocchi da 128 byte di cui è composto il file e lascia
all'utente di riempire il file con EOF o trovare un altro metodo per 
conoscere esattamente dove finisce il file.


FILE *out;
int i;
out = fopen("pippo.dat", "w");
for (i=0; i<=0xff; ++i)
    fprintf(out, "%c", (char)i);
fclose(out);
 
Controlli a parte, questo per quello che chiedi funziona *sempre*.
Su come il sistema operativo o i programmi applicativi che prendono in carica
la gestione del file decidono di interpretarlo è ovviamente impossibile
rispondere se non dici cosa ci vuoi fare con questo file o dove lo vuoi
usare.
 
Ciao!

 dire "se ritorna"?
 
E' un refuso.. Volevo scrivere "ritorna non zero se ha successo", nel senso
che torna zero se non c'è un glifo associato al carattere.
 
Ciao!


E' un problema di filesystem, direi..
Comunque nel caso del CP/M mi aspetterei che la libreria standard gestisca
questo "problema" in maniera che per il programmatore C sia totalmente 
trasparente.
Ciao!

 senso che torna zero se non c'è un glifo associato al carattere.
 
Ok. A volte capita.

 trasparente.
 
CP/M era un sistema operativo degli anni ottanta. E no, non gestiva 
la cosa in maniera trasparente, anzi appioppava tutta la faccenda 
all'utente. Per CP/M i file erano sequenze di blocchi ed un blocco 
era di 128 byte (allora i settori dei floppy da 8" erano 128 byte)
punto e basta. La frammentazione interna era una tua rogna. Se 
l'ultimo blocco era solo parzialmente pieno te la sbrigavi da solo.
 
 

 particolari, alla maniera di tutti gli altri caratteri.
 
Ops Stavo penando all'altra replica. Discorso valido ma c'entra poco 
con la risposta.
 
 

 
 
Stiamo dicendo la stessa cosa, allora ;)
Ciao!

 Stiamo dicendo la stessa cosa, allora ;)
 
No. Molti sistemi operativi hanno visioni particolari dei file: 
sequenze di record, sequenze di oggetti, unione di due o più
flussi. Per Unix i file sono sequenze di caratteri crudi, ogni 
altra interpretazione è lasciata all'utente. Per C no: esistono 
i file binati ed i file testuali. Non mi pare tu dica questo.
 
Però, e qui penso che diciamo la stessa cosa, cosa vuol dire file 
testuale o file binario dipende dal sistema operativo. Attualmente 
su sistemi POSIX C implementa un solo tipo di file, il file binario,
su altri sistemi operativi o su compilatori storici le cose potrebbero
essere diverse.

 essere diverse.
 
Yup.
 
Ciao!

 male.
 
 
Fa niente.  A volte anche io.  Cool and Peace
 
 


Ok, hai ragione, ogni OS fa a modo suo
Avrei dovuto specificare 'OS moderni'
Per carità, lasciamo da parte i vetusti CP(M e MS/DOS che tra l' altro 
non sono e non erano all' epoca dei veri sistemi operativi nel senso che 
intendo (scheduling, access protection,virtual memory,ecc..)
 
 
 
 

 Il C è particolare, distingue tra file binari e file di testo
 
Per la precisione, la libreria std distingue ...
Il linguaggio non sa cosa sia un file o un carattere (in realtà è un byte)
 
 
 

Addì  martedì 27 novembre 2012 15:55 enoquick scrisse:

Fatto bene a correggere. Quindi avrei dovuto dire:
 
Il C è particolare perché non sa nemmeno cosa è un file, lascia alla
libreria standard di occuparsi dell' input/output, definire le funzioni 
printf, scanf et cetera, e distinguere tra file binari e di testo. Inoltre
le specifiche lascino all'implementazione della libc (all'incirca) di 
definire... 
 
A volte si dimentica che il C è veramente particolare.

 intendo (scheduling, access protection,virtual memory,ecc..)
 
Conosco persone che ti fucilerebbero alle spalle. Definire cosa è un
sistema operativo è una cosa spinosa, neanche Tanenbaum ci tentò,
ma ti faccio notare che quei sistemi operativi:
 
  1) avevano uno scheduler, primitivo ma adatto ad un sistema 
      monotasking.
 
  2) erano multiutente ed avevano delle politiche di protezione e
     sicurezza.
 
e che:
 
  3) non puoi nemmeno parlare di protezione o di memoria virtuale
     se non hai l'hardware per implementarli
 

      se non hai l'hardware per implementarli
 
Questo non sono d'accordo. La prima versione di Minix implementava i ring di
protezione (che sugli x86 sono hardware dai 386) via software. Tempi addietro
erano i produttori di hardware che cercavano di stare dietro al software
implementando in hardware le feature più diffuse: oggigiorno è quasi utopia,
putroppo (anzi, la rimozione del Jazelle sugli ARM è una prova che si va verso
la direzione opposta).
 
In generale comunque questi sistemi, per la complessità insita nel processo 
d'innovazione che portavano, erano molto più difficili da capire di quanto lo
è un sistema moderno, che si sforza di essere "standard".
 
Ho un po' di nostalgia. :'-)
 
Ciao!

 La prima versione di Minix implementava...
 
intendevo Multics, naturalmente (Minix, per quanto carino, e non me ne voglia
Tanenbaum, di innovativo aveva ben poco - fermo restando che ha insegnato cos'è
un SO a generazioni di programmatori).
 
Ciao!


Perfetto, sono contento che ogni tanto andiamo d'accordo
 


Può anche essere ma il giudizio è mio
Per me un OS deve implementare una macchina astratta su un hw
I processi utente non accedono direttamente all' hw ma attraverso le
API del sistema operativo
MSDOS dava come astrazione solo il filesystem, il resto alcune API 
rudimentali e  basta
Un processo poteva fare dell' hw della macchina quello che voleva, 
quindi che sicurezza puoi implementare in un sistema del genere ?
Indentiamoci, non sto denigrando l' msdos; con l' hw di quei tempi non è 
che si potesse  fare molto, ma comunque minix sui 286 e xenix sui 386 
giravano
 
 
 
 

 la direzione opposta).
 
Non  dovrebbe essere stato rimpiazzato da ThumbEE da ARMv7 in poi? A 
differenza di Jazelle, ThumbEE dovrebbe essere documentato e supportare 
diversi tipi di bytecode.

 giravano
 
A parer mio, far girare un SO multiutente su un PC dove lavora un utente solo
è una str****ta, ma così ormai va il mondo.
 
Ciao!


L'idea iniziale era quella di far girare bytecode Java in hardware. Adesso
come adesso tutti scrivono (e/o compilano) solo in codice ARM senza tener
conto nè di T né di EE neppure per le ISR di IRQ! :-)
Ciao!

 usare.
 
lo scopo sarebbe effettuare da riga di comando specifiche operazioni di lettura e scrittura dei bit su un file creato dal programma stesso e vorrei che funzionasse sia su linux che su windows, tutto qui

 funzionasse sia su linux che su windows, tutto qui
 
Allora vai tranquillo ;)
Finché usi le funzioni della libreria standard che scrivere/leggere singoli
bytes su un file binario puoi usare sia lo stesso codice che lavorare su file
creati dai due ambienti separatamente.
Ciao!

 lavorare su file creati dai due ambienti separatamente.
 
L'unica avvertenza è aprire il file come binario; Windows dovrebbe 
essere POSIX e quindi dovrebbero valere le stesse cose dette per
Linux a proposito di EOF. Comunque se per te il problema dell'EOF 
è importante apri il file come binario.
 
 
 
 
 


Mi spiace dover intervenire (poi sembra che ce l' abbia con tè, cosa non 
vera ) ma windows non è posix
E' vero che esiste un sottosistema posix ma normalmente l' utente non lo 
installa e non lo usa
L' ambiente comunemente usato si chiama win32 o win64 mentre le librerie 
std C sono ansi non posix
Per tutto il resto basta tenere conto che aprire in win32 o win64 un 
file in modalità binaria un eventuale newline è visto come due bytes 
\r\n al contrario di unix che è solo \n (in unix non c'è differenza tra 
modalità binaria e caratteri per quanto riguarda il newline)
L' EOF corrisponde sempre ad un byte non esistente in quando è 
normalmente un int a -1 (almeno 0xFFFF), ma nei programmi portabili non 
si deve assumere ciò ma, appunto, usare la costante EOF
EOF è indifferente dalla modalità di apertura (testo o binario) usata
 
 
 
 
 
 
 


No aspetta, qui è tutto sbagliato.
1- Windows *non* è POSIX. Non ci prova neppure. (1)
2- Il "problema dell'EOF", come hai anche scritto te nei post precedenti
argomentanto la motivazione (perché ora scrivi così, infatti?), è indifferente
sia per Win che per qualsiasi forma di *nix.
 
Ciao!
 
(1) Ci sono delle librerie di terze parti (ad es. cgywin) che fanno da wrapper
per avere un sistema POSIX (che funziona neppure tanto bene).

 
 
Intervengo anche qui.
I compilatori MS non sono più compatibili ANSI C già da un bel po': hanno
spostato tutti gli sforzi sulla parte C++, lasciando la parte C in una specie
di limbo (semplicemente non la sviluppano e non la mantengono più).
Ci sono tuttavia una serie innumerevole di compilatori+libc compatibili per
windows, ma di terze parti.
Ciao!


 
Esiste anche un sistema posix proprio fornito da Microsoft ma occorre 
scaricarlo e installarlo
 


Le cygwin sono un classico ambiente per win32/64 compreso di compilatore gcc
Altri compilatori non ne conosco e sinceramente non sono molto 
interessato allo sviluppo sotto win32/64, preferisco e uso linux
 
 

 indifferente sia per Win che per qualsiasi forma di *nix.
 
Nessuno legge i manuali come al solito. Sta scritto chiaramente che 
se il programma deve essere portabile il file deve essere aperto in 
modo binario.
 
E non vedo perché questo dovrebbe essere in contraddizione con 
quanto scritto precedentemente.
 
 
PS E non ti dico quali manuali. ;-)
 

 EOF è indifferente dalla modalità di apertura (testo o binario) usata
 
 
Il punto è che se apri un file in modo testo tu lasci al sistema operativo 
ed alle librerie questa problematica ed ottieni un programma non portabile.
Se invece apri un file binario devi tener conto di tutto questo e di altro
ancora però ottieni un programma prevedibile e portabile. Per questo 
i manuali delle open consigliano di aprire sempre il file come binario
quando si prevedono problemi di portabilità.
 
 


 
Non so quali manuali tu abbia letto ma se un file di testo
che lo vuoi leggere a righe o carattere per carattere, se lo apri in 
modalità binaria perdi proprio la compatibilità
La modalità binaria va bene quando vuoi leggere un flusso di bytes
 
 


 
Su questo punto ti ho risposto in un altro thread
 

 PS E non ti dico quali manuali. ;-)
 
Se sono manuali MS, meglio che cambi sorgente d'informazioni! :-)
 
Ciao!

 std del C, che tra l' altro è uno standard dell' ANSI
 
 
E qui hai ragione . Pero ti faccio notare che lo standard POSIX e 
non l'ANSI impone che non ci sia distinzione tra file binari e di testo.
 

 La modalità binaria va bene quando vuoi leggere un flusso di bytes
 
In effetti fmassei è stato chiaro: se devi leggere un file testo 
bastano le funzioni della libreria standard e stop.
 
Invece l'OP vuole trattare con i caratteri di controillo, con gli 
end of file, con i newline ed i carriage return e quindi già vede 
il file come flusso di bit; a questo ponto lo apre come file binario.
 


Che è utile se il file viene letto come binario (non devi smazzare il 
\r\n o il solo \n)
Perfetto, è chiaro e sono d'accordo
 


Oh my.
Sembra di parlare in un NG di politica :-)
Facciamo parlare il codice, OK?
 
#define FILENAME "pippo.dat"
#define WRITEFLAG "w"
#define READFLAG "r"
 
FILE *out, *in;
int i;
char buf[100];
out = fopen(FILENAME, WRITEFLAG);
for (i=0; i<sizeof(buf); ++i)
    buf[i] = (char)i;
fwrite(buf, 1, sizeof(buf), out);
fclose(out);
/* ... */
in = fopen(FILENAME, READFLAG);
fread(buf, 1, sizeof(buf), in);
for (i=0; i<sizeof(buf); ++i)
    printf("%02x", buf[i]);
fclose(in);
/* ... */
 
Adesso, per piacere, facciamo gli esempi con questo codice? Su cosa fa su win,
su unix, su linux o dove ci pare a noi? Non so neppure se è giusto perché l'ho
scritto di getto, per cui magari si corregge, si aggiunge o si commenta.
 
Il punto qual'è? Che se WRITEFLAG come "wb" o "wt" o "w" (e parallelamente la
READFLAG) il programma cambia behaviour?
Non credo proprio.
 
Ciao!

 commenta.
 
Più che sui vari sistemi operativi dovresti provare sui vari compilatori
includendo anche i vecchi come Wacom, Turbo C, SAS/C, Tiny C, Clang,
Interactive C... 

 Non credo proprio.
 
Dunque, se utilizzi lo stesso formato (text o binary) sia per leggere che per
scrivere, e scrittura e lettura avvengono su architetture compatibili
il risultato viene identico (e ci mancherebbe anche...)
 
Se usi formati diversi, ad esempio scrivi testo e leggi binario, rischi di
avere risultati diversi, ad esempio, se esegui il programma su windows
ottieni due stampe diverse perche' la fwrite su uno sstream di testo su
windows sostituisce ogni \n con una coppia \r\n cosa che non avviene se
lo stream e' binario:
 
wt + rb
000102030405060708090d0a0b0c0d...
 
wb + rb
000102030405060708090a0b0c0d0e...
 
wt + rt
000102030405060708090a0b0c0d0e...
 
Come vedi la fwrite su uno stream di testo scrive un byte in piu' che la
lettura in binario evidenzia MA la lettura in testo invece NO perche' 
effettua la conversione al contrario: ogni volta che trova \r\n converte
in un unico \n
 
Su sistemi Unix questo non si vedrebbe perche' la norma e' di usare \n come
carattere di andata a capo quindi la conversione di fatto non fa nulla.
 
Sui sistemi Mac dove si usa '\r' lo vedresti di nuovo ma non con un byte
in piu' ma con un byte diverso.
 
La situazione si complica ulteriormente quando trasferisci file tra architetture
diverse: se prendi un file di testo da windows e lo porti su unix, ti compaiono
dei caratteri in piu' in pratica raddoppiandoti le andate a capo
(questo avveniva in passato, al giorno d'oggi penso sia standard che la libreria
quando legge uno stream in modalita' testo e' in grado di capire che tipo di
andata a capo e' stata usata e si adegua in modo piu' intelligente)
 
ciao
Giacomo

 
 
Ok, ho ripreso lo standard, e mi correggo da solo :)
 
I punti in questione sono #4.9.[1,2], #A6.3.13 e la nota 99, che riassumo:
Nel caso il file (testo o binario) sia associato ad un text-stream (quindi non
un file fisico ma, ad esempio, un terminale), in caso di:
- presenza di caratteri non in {stampabili, tab, newline}
- presenza di spazi prima del carattere di fine riga
- assenza del carattere di fine riga come ultimo carattere
la rappresentazione potrebbe essere diversa (e questo lo sapevamo), ma anche
il comportamento delle funzioni di libreria sono implementation-defined.
Nel nostro caso, visto che non stiamo rispettando i punti 1 e 3, dobbiamo
essere sicuri che lo stream associato al file non sia un text-stream.
 
Incredibilmente, e questo non lo sapevo, anche se le operazioni su un
file di testo (associato ad uno stream binario) sia o meno "tradotta" allo
stesso modo dalle funzioni di libreria in lettura e in scrittura è
implementation-defined (quindi, in teoria, non posso *mai* aspettarmi di
rileggere esattamente quello che ho scritto su un file di testo, caratteri
stampabili a parte).
 
Ciao!
 
P.S. Se qualche anima pia ha l'ultimo standard sottomano magari si può fare
un confronto con C99, ma non penso cambi di molto ;)

 stampabili a parte).
 
Quindi ripetiamo e giungiamo alla versione finale (si spera):
 
Se si vuole solo leggere o scrivere delle stringhe su un file, apri
il file in modo testo e usa le funzioni di libreria.
 
Se invece si vuole garantire la massima portabilità, trattare 
direttamente i caratteri di controllo o essere sicuri del formato 
del file, apri il file in modo binario.