Newsgroup: it.comp.lang.c
Titolo: Memorizzare testo immesso da tastiera

Salve,
   in alcuni linguaggi e' possibile effettuare l'input da tastiera di
stringhe di dimensioni arbitrarie memorizzandole in una variabile
stringa che possiamo immaginare di chiamare str. La variabile str
conterra' tutto il testo inserito attraverso la tastiera, spazi
compresi, e terminera' quando incontera' un newline.
Poiche' il tipo stringa in C non e' un tipo elementare ma e' trattato
come un array di caratteri, vorrei sapere come fare per ottenere lo
stesso risultato che si ottiene negli altri linguaggi.
 
In pratica, in un ipotetico programma, all'utente viene chiesto
d'inserire del testo che il programma dovra' memorizzare, ma al
programma non e' nota a priori la lunghezza del testo che l'utente
inserira', quindi non e' utilizzabile un array, non potendo stabilire
preventivamente la grandezza da assegnare all'array.
Credo che un problema analogo si ponga anche volendo utilizzare la
malloc, poiche mi sembra non ci sia modo di calcolare il numero di
caratteri di cui e' composto il testo che verra' immesso e, di
conseguenza, lo spazio da assegnare all'atto della chiamata della
malloc.
Mi potreste indicare la soluzione di questo problema?

 Mi potreste indicare la soluzione di questo problema?
 
Il modo piu' semplice e' usare "realloc".
Vedi questo esempio se ti va bene:
http://programmazione.html.it/guide/lezione/1110/allocazione-dinamica-della-memoria-funzione-realloc/
 
 
Ciao.


Ciao e grazie della risposta,
   vorrei capire meglio una cosa: il codice che mi hai segnalato, aumenta la dimensione dell'array ogni volta che introducendo un nuovo intero lo spazio non e' piu' sufficiente. Attraverso la if verifica che lo spazio disponibile e' insufficiente e con la realloc riassegna, a patto che vi sia spazio, un'area di memoria (immagino ininterrotta) in cui memorizzare l'array, questa volta piu' grande di una quantita' pari a dimbloc. Successivamente, l'intero, posto nel buffer dello input stream, viene memorizzato nell'ultima locazione dell'array.
 
Questa soluzione, che in questo esempio va bene, presuppone pero' che sia nota a priori la dimensione dell'informazione presente nell'input stream. In questo caso mi sembra, infatti, che il programma possa sempre prevedere che in input vi sara' un intero la cui dimensione e', in base alla specifica implementazione del compilatore, prestabilita. Se ad esempio: s'immagina che a un dato di tipo int sia riservato uno spazio pari a 4 byte, ogni volta la dimensione dell'array verra' incrementata di 4 byte (cioe' il valore di dimbloc). Questo e' possibile perche' viene immesso sempre e solo un int alla volta la cui dimensione e', nel mio esempio, sempre di 4 byte. Ma, se nell'input stream c'e' una stringa, questa se e' fatta di 5 caratteri sara' grande 5 byte, ma se e' di 8 carratteri sara' di 8 byte. L'arbitrarieta' della dimensione della stringa presente nel buffer dell'input stream e la conseguente difficolta' nel calcolarla e' proprio lo scoglio principale da superare.
 
Un ulteriore difficolta' e' il fatto che il buffer, credo venga scaricato tutto nella variabile direttamente, quindi non posso leggere un carattere alla volta dal buffer. Cioe', in pratica, se ho immesso da tastiera la parola "salve", nel buffer ci saranno proprio i caratteri 's', 'a', 'l', 'v', 'e', ed il carattere che corrisponde alla pressione del tasto invio, che rappresento attraverso '\n' (rappresentato in modi diversi a seconda dei vari sistemi operativi, nei sistemi *nix like potrebbe essere solo un line feed LF mentre nei sistemi windows potrebbe essere un carrige return + il line feed CRLF). Dopo aver immesso la parola 'salve' e premuto invio, credo che non posso leggere un carattere alla volta, tralasciando tutti quelli letti precedentemente, ed aumentare lo spazio con una tecnica analoga a quella vista nel caso dell'esempio che mi hai suggerito. Non so infatti come gestire il buffer dell'input stram, potrei trovarmi a leggere sempre il primo carattere e quindi dovrei creare una funzione che svuoti il buffer (una cosa come fflush ma per lo stream di input) ma un carattere alla volta.
 
Trovo strano che il problema che ho posto all'inizio non sia ampiamente ed esplicitamente documentato e riportato addirittura nei libri sul C piu' elementari.

 Trovo strano che il problema che ho posto all'inizio non sia ampiamente ed esplicitamente documentato e riportato addirittura nei libri sul C piu' elementari.
 
Mi stai che ti stai fasciando la testa prima di romperla.
 
1) Innanzitutto dovresti chiarire che funzioni usi per leggere l'input 
stream e che sistema operativo usi.
Ti accorgerai allora che le funzioni con cui leggi i caratteri letti 
avranno un limite massimo di caratteri ritornati, sicuramente 
documentato, non puo' mica essere infinito :)
A quel punto tari il tuo buffer alla stessa dimensione o leggermente 
superiore del buffer interno del sistema operativo.
 
2) Se infine volessi comunque un sistema per archiviare una lista 
ordinata di buffer arbitrariamente lunghi ti crei una lista concatenata 
di buffer.
struct buffer_st {
    char *buffer;
    struct buffer_st *next;
};
 
Ciao

 documentato, non puo' mica essere infinito
 
Aggiunco per completezza che in C le funzioni per leggere lo standard 
input hanno di solito la versione in cui passi TU al sistema operativo 
il buffer (con la dimensione) da riempire, e la usi piu' volte fintanto 
che il sistema operativo ti segnala che non c'e' piu' da leggere, vedi 
poll, select, ...
 
Ciao.

 
Questa e' una soluzione, migliore della realloc in termini di gestione della memoria, perche' si aggira il problema di dover avere un blocco di memoria senza interruzioni sufficientemente grande da contenere ogni volta la riallocazione dell'array, in pratica riduce il problema di frammentazione della memoria, ma non risolve il problema di come contare i caratteri inseriti da tastiera.
 
 
Ho letto i suggerimenti che mi dai successivamente sulle funzioni poll e select ma non le conosco e sebbene abbia letto la relativa pagina man non riesco a capire come si possano utilizzare nel caso specifico.
 
Continuo a stupirmi che una funzione che faccia quello che ho immaginato (memorizzare i caratteri inseriti da tastiera) sia qualcosa di molto piu' complesso di quanto immaginassi e non ci sia qualcosa di gia' fatto che faccia tutto cio'. Ma ammesso che si debba costruire uno strumento ad hoc non e' neanche affrontato teoricamente evidenziandone i pro e i contro delle soluzioni possibili, eppure il problema e' uno scoglio che in vari programmi dev'essere superato.

 dev'essere superato.
 
È stato superato leggendo il buffer a pezzi, forse non ne parlano perché 
non è un gran problema.
Se ti sembra un problema poni tu il vincolo, nel file LEGGIMI scrivi 
"questo programma legge fino a KEYBOARD_BUFFER_SIZE caratteri", metti un 
bel #define e usi un buffer fisso.

 Continuo a stupirmi che una funzione che faccia quello che ho immaginato <CUT>
 
Ma no dai, il problema non e' la gestione della memoria in C, che 
comunque pare che non conosci, e' che non conosci le funzioni per 
leggere lo standard input.
Vedo di farti un esempio semplice appena riesco.
 

 bel #define e usi un buffer fisso.
 
Beh vorrei superare questo vincolo, se avessi voluto mettere un limite
fisso a cio' che l'utente poteva immettere mi bastava usare un'array
statico e stop non avrei neppure rivolto la domanda al NG.
Se volessi memorizzare il testo di una lettera potrei mai dire
all'utente non devi scrivere piu' di n caratteri?

On 25 Mar, 23:21, coder <c...@nomail.not> wrote:

Si la mia conoscenza della gestione della memoria e' limitata a quanto
indicato nel K&R 2ed e cioe' le funzioni malloc, calloc, realloc e
free sono certo vi siano altre cose piu' sofisticate ma non ho trovato
nulla di didattico. Se esiste altro e mi indichi una fonte saro' ben
lieto di studiarmi il relativo materiale, ovviamente cio' vale anche
per le funzioni che leggono dallo standard input. Ma cercare di capire
dalle pagine man vorrei fosse la mia ultima spiaggia.
Ciao.

 Mi potreste indicare la soluzione di questo problema?
 
Anche se potrà sembrare un problema banale, l'implementazione di una 
funzione che lo risolve non lo è altrettanto.
 
Spesso si preferisce una allocazione statica (un array) 
iper-dimensionato per evitare implementazioni più robuste e affidabili, 
ma al contempo più complicate da scrivere.
 
Ti ha suggerito bene coder, quando ti ha parlato della realloc, ma non 
ho capito perchè la discussione è andata avanti su un'altra strada.
 
La realloc viene usata proprio per risolvere problemi come il tuo: una 
riallocazione dinamica di un buffer, generalmente in un'*altra* zona di 
memoria con copia automatica del vecchio contenuto.
 
In pratica hai bisogno di una funzione che legge N blocchi, dove N è 
fisso (nel tuo caso N caratteri da stdin).  Se negli ultimi N blocchi 
non c'è l'ultimo, bisogna riallocare il buffer per leggerne altri M, con 
M fisso (dove M può essere uguale a N, o anche diverso, a seconda della 
nuova dimensione del buffer).
 
In particolare, il tuo problema si dovrebbe risolvere in questo modo:
 
char *
mygets(char *mystring, size_t bufsize, FILE *stream)
{
   fgets(mystring, bufsize, stream);
   while(mystring[strlen(mystring) - 1] != '\n') {
     mystring = realloc(mystring, 2 * bufsize);
     bufsize *= 2;
     fgets(&mystring[strlen(mystring)],
       bufsize - strlen(mystring), stream);
   }
   return mystring;
}
 
Scegli una prima dimensione fissa per un array di char (io cercherei di 
scegliere una dimensione che dovrebbe permettermi di leggere la maggior 
parte degli input senza necessità di riallocare) e lo passi (puntatore e 
dimensione) alla funzione mygets().  Ovviamente l'array di char deve 
essere allocato dinamicamente con una malloc() per poter essere 
eventualmente riallocato.
 
La funzione si preoccupa di riallocare automaticamente l'array di char, 
se necessario, cioè quando l'ultimo carattere letto dopo la prima 
fgets() non è un '\n'.  Qui la strategia che ho adottato è raddoppiare 
la dimensione del buffer ogni volta (si potrebbe aumentare sempre di un 
certa quantità).
 
La funzione ritorna il puntatore alla stringa che contiene tutto l'input 
fino al '\n'.  Si potrebbe fare un'ultima realloc() per ridimensionare 
il buffer esattamente alla dimensione del testo, ma potrebbe essere 
superfluo.
 
Non ho mostrato un passaggio essenziale: quando riallochi, la realloc() 
potrebbe non andare a buon fine e, in quel caso, perderesti il puntatore 
al vecchio buffer, che rimane ancora allocato in memoria, senza poterlo 
liberare (sempre che non ti salvi in una variabile temporanea il suo 
puntatore prima di richiamare la realloc).

 In particolare, il tuo problema si dovrebbe risolvere in questo modo:
 
Va benissimo, io per diletto ne ho fatto una versione
basata su select/read, anche qui mancano dei controlli e il buf[80]
va tarato sulla dimensione media.
 
========================================================================
// Ritorna un puntatore alla riga letta dallo stdin.
// La riga termina con '\n', e' 0-terminated, e va liberata con free().
char *read_line()
{
     fd_set fdset;
     int n, ret, linelen = 0;
     char buf[80], *line = NULL;
 
     while (1) {
         FD_ZERO(&fdset);
         FD_SET(STDIN_FILENO, &fdset);
 
         ret = select(STDIN_FILENO + 1, &fdset, 0, 0, NULL);
         if (ret > 0) {
             if (FD_ISSET(STDIN_FILENO, &fdset)) {
                 n = read(STDIN_FILENO, buf, sizeof(buf));
                 if (n > 0) {
                     //printf("DEBUG(%d): %.*s\n", n, n, buf);
                     line = (char *)realloc(line, linelen + n + 1);
                     memcpy(line + linelen, buf, n);
                     linelen += n;
                     if (buf[n - 1] == '\n')
                         break;
                 }
             }
         } else if (ret < 0) {
             perror("select");
         }
     }
     line[linelen] = 0;
 
     return line;
}
========================================================================


Ringrazio sia te che pozz per le risposte e il codice che avete
scritto. Ma vi ringrazio maggiormente per gli spunti che mi avete dato
perche': se gli esempi sono del buon cibo che ti sfama al momento,
ogni cosa  utile per capire meglio certi argomenti, sono una lezione
di cucina che ti sfamera' per molto tempo ;-)

 Mi potreste indicare la soluzione di questo problema?
 
Ciao,
l'esempio che ti propongo legge dallo standard input fino a quando non 
incontra la fine del file (^D per *nix o ^Z per win/dos). Nella funzione 
ReadIn() la dimensione dei blocchi allocati è impostata a 10 per le 
prove ma puoi impostarla a tua discrezione.
 
#include <stdio.h>
#include <string.h>
#include <malloc.h>
 
void main(void);
char *ReadIn(void);
 
void main(){
     char *txt;
 
     if(!(txt = ReadIn()))
         printf("\n"
                "Text size: %d"
                "\n\n"
                "%s"
                "\n", strlen(txt), txt);
}
 
char *ReadIn(){
     char *in;
     int BlkSz = 10, tBlkSz = BlkSz, i;
 
     if(!(in = malloc(tBlkSz)))
         return (char *)NULL;
 
     for(i = 0; (in[i] = getchar()) != EOF; i++)
         if(i == tBlkSz - 1) {
             tBlkSz += BlkSz;
             if(!(in = realloc(in, tBlkSz)))
                 return (char *)NULL;
         }
     in = realloc(in, i + 1);
     in[i] = '\0';
     return in;
}

 }
 
M'è scappato un punto esclamativo nella 'if'. Sorry.

 Mi potreste indicare la soluzione di questo problema?
 
Ciao,
l'esempio che ti propongo legge dallo standard input fino a quando non 
incontra la fine del file (^D per *nix o ^Z per win/dos). Nella funzione 
ReadIn() la dimensione dei blocchi allocati è impostata a 10 per le 
prove ma puoi impostarla a tua discrezione.
 
#include <stdio.h>
#include <string.h>
#include <malloc.h>
 
void main(void);
char *ReadIn(void);
 
void main(){
     char *txt;
 
     if(txt = ReadIn())
         printf("\n"
                "Text size: %d"
                "\n\n"
                "%s"
                "\n", strlen(txt), txt);
}
 


 }
 
Il controllo d'errore della prima realloc() non dovrebbe essere 
corretto.  Se la funzione ritorna NULL, il blocco che avevi allocato con 
la prima malloc() (o una precedente realloc() che era andata a buon 
fine) non sarà più indirizzabile né si potrà invocare la free() di essa.
 
Perchè hai saltato il controllo d'errore sulla seconda realloc()?

              in = realloc(in, tBlkSz)))
 
Assegnare subito alla setessa variabile che stai passando alla realloc non 
mi sembra una buona pratica perche' potrebbe portare ad un memory leak,
io userei una variabile aggiuntiva per poter eventualmente
deallocare la memoria nel caso in cui realloc fallisca.
 
p = realloc( in, tBlkSz );
if (p)
  in = p;
else
  // qui 'in' ha ancora un valore valido quindi posso fare free(in);
 
 
ciao
Giacomo
 


Visto che non mi sembra l'abbia scritto nessuno (giustamente, visto che quello che chiedi è un esercizio tipico in C), se sei su linux e semplicemente vuoi qualcosa che funziona "man readline".
Ciao!

 Perchè hai saltato il controllo d'errore sulla seconda realloc()?
 >
 
Perché se durante il ciclo for non controllassi l'errore rischierei di 
andare a zappare per la RAM scrivendo in zone che non mi appartengono, 
mentre quella fuori ciclo, per come è impostato l'algoritmo, de-alloca 
sempre, quindi non darà mai errore e nel caso in cui andasse in errore 
ritornerebbe un puntatore NULL ed errno settato a ENOMEM quindi tanto 
vale che che il test lo faccia il chiamante ;-)
 
Comunque era solo un esempio.
 
Saluti.
 


:-(
Secondo te ENOMEM allora che ci sta a fare? Tanto valeva per la libreria standard chiudere direttamente il chiamante, o no? :-(
ENOMEM va gestito, come tutti gli altri errori, e questo mito che se malloc() e amici falliscono tanto la situazione è disperata è un'enorme ca**ata.
Ciao!

 Comunque era solo un esempio.
 
Sicuramente ed hai fatto bene a postare la tua idea. Ci mancherebbe.
Volevo solo puntualizzare che era meglio stare attenti ad alcune cose 
della tua funzione che potevano essere migliorate.
 
 


 
Allora ti faccio una puntualizzazione anch'io. Scrivo librerie in C da 
un quarto di secolo e quando pubblico pezzi di codice è per aiutare chi 
studia o è in difficoltà altrimenti rilascerei degli obj e non codice 
sorgente adattabile alle necessità.


PS:
Non ne sei certo? Metti la dimensione del blocco ad 1 e provalo su un 
sistema *nix: se non va in memory fault dovrebbe convincerti.


Mica l'ho capita, questa. Se su iclc posti degli obj (chissà poi per
quale piattaforma),
penso nessuno li considererebbe.
 
Mi dispiace tu ti sia offeso, non volevo assolutamente.

 sistema *nix: se non va in memory fault dovrebbe convincerti.
 
Il fatto che una volta, su un particolare sistema ed una particolare
implementazione C
non vada in memory fault, non vuol dire che si comporti sempre così.
 
Ecco un estratto dal manuale delle libc GNU:
 
You can also use realloc to make a block smaller. The reason you would
do this is to avoid tying up a lot of memory space when only a little
is needed. In several allocation implementations, making a block
smaller sometimes necessitates copying it, so it can fail if no other
space is available.
 
Nota il "In *several* allocation implementations".

>

Mica l'ho capita, questa. Se su iclc posti degli obj (chissà poi per
quale piattaforma),
penso nessuno li considererebbe.
 
Mi dispiace tu ti sia offeso, non volevo assolutamente.
 
P.S.: Avevo già risposto tramite Google Gruppi, ma sembra che non venga 
visualizzata correttamente da tutti. Scusate per chi vedrà un mio doppio 
post.
 
 


Il fatto che una volta, su un particolare sistema ed una particolare
implementazione C non vada in memory fault, non vuol dire che si 
comporti sempre così.
 
Ecco un estratto dal manuale delle libc GNU:
 
"You can also use realloc to make a block smaller. The reason you would
do this is to avoid tying up a lot of memory space when only a little
is needed. In several allocation implementations, making a block
smaller sometimes necessitates copying it, so it can fail if no other
space is available."
 
Nota il "In *several* allocation implementations".
 



 
Se proprio vuoi ricercare potresti informarti su quando si verificano 
quei 'sometimes'; potresti aver delle sorprese.
 
Chi fa come te mi ricorda le "vecchie mai state mogli" di De Andrè, 
sempre pronte per giudizi e sentenze ma nel momento di aiutare, sempre 
solo fievoli consigli lasciati cadere dall'alto.
Se non ti convince mettici la 'if'; non comportarti da troll perché 
rischi di diventarlo e rischi che ti si aspetti al varco del primo 
errore (google lo usano in molti).
 
Passati una buona giornata, troll.
 
plunk

 ENOMEM va gestito, come tutti gli altri errori, e questo mito che se malloc() e amici falliscono tanto la situazione è disperata è un'enorme ca**ata.
 
Pienamente d'accordo :-)
 

Il 28/03/2012 10:43, 
giacomo.de...@gmail.com
 ha scritto:

Infatti, avevo giŕ fatto questa osservazione, ma lui non č d'accordo.
 
 

 plunk
 
Mi sa che hai preso proprio un granchio e stai esagerando.  Penso che 
stai facendo una figuraccia qui dentro.


Hei massei, da te proprio non me l'aspettavo.


PS: ho risposto al thread di pozz perché il tuo messaggio non lo sto 
vedendo (non so gli altri). E' noto che rispondendo da google si 
rovinano gli alberi dei thread e mi domando perché insistiate. Se pozz 
non sta trollando gli risponderò seriamente ma trovo strano che 
qualcuno, resosi conto che da google si verifichino dei 
malfunzionamenti, si ostini nell'utilizzo... a meno che non sia 
intenzionale.


A parte la brutta piega che sta prendendo il thread (che spero riprenda la calma che contraddistingue iclc) sono ancora convinto di quello che ho scritto.
Se, come hai detto, scrivi librerie da tanto tempo ti sarà successo di finire la memoria centinaia se non migliaia di volte: magari nel caso delle tue librerie la scelta migliore per l'utilizzatore era un abort(), ma di sicuro ci sono software in cui ripulire e tornare un errore è una pratica migliore (ti posso fare esempi a sfare).
Per quanto riguarda l'overbound è vero, spesso su linux/unix non ha effetti *però* è un UB e quindi rende tutto il codice potenzialmente instabile, oltre a far lamentare strumenti di analisi (come valgrind) e in qualche caso anche i check statici (come lint) - il che vuol dire che stai volontariamente aggiungendo rumore fastidioso in caso di ricerca di "vero" bug.
In ogni caso, tra fare le cose come dici (e come fanno in molti, IMHO purtroppo) e come "andrebbero" fatte per eliminare UB non ci sono grandi differenze, quindi non vedo perché scegliere la prima strada.
Ciao!

 
Finalmente un commento degno di tal nome!
Scrivere librerie, secondo me, spesso viene confuso col saper 
ammucchiare obj in un file unico ma è molto più complicato. A mio parere 
esistono due tipi fondamentali di librerie: la prima è quella che 
permette a chi sviluppa di intefacciarsi a periferiche senza la 
necessità di doverne acquisire una completa padronanza (es: usi le 
funzioni di libreria per non doverti riscrivere un protocollo di 
comunicazione. Esagero ma immagina di doverti riscrivere il tcp/ip), 
l'altra tipologia è relativa alla compatibilità che riguarda il caso in 
cui una modifica hw/sw renda necessaria una nuova libreria che permetta 
ai programmi realizzati per vecchi contesti di poter, ricompilati, esser 
riutilizzati in contesti nuovi senza venir ritoccati. Quest'ultima 
parte, sempre secondo me, è parte di ciò che mi è stato contestato 
asserendo che in contesti differenti (es: un altro sistema operativo) le 
funzioni potrebbero avere comportamenti differenti. Se ciò dovesse 
accadere la colpa non potrebbe che ricadere sul 'libraiolo' che non è 
stato in grado di riprodurre il funzionamento. Cosa che mi è capitata.
Tornando a quelle 4 righe di codice che hanno aperto il flame e che mi 
son pentito di aver pubblicato, la mia difficoltà principale sta nel far 
capire che l'ESEMPIO che ho scritto (mentre mangiavo un panino) era 
incentrato sul far capire all'OP come realizzare ciò che desiderava ed 
ho, sinceramente, posto pochissima attenzione all'allocazione. Avrei 
preferito mille e mille volte che mi fosse stato contestato l'algoritmo 
o che non mi fosse data risposta alcuna.
Affermazioni del tipo: "dipende dal sistema" oppure "postare il file 
oggetto", se mi permetti, è una gratuita offesa all'intelligenza altrui 
e mi porta a pensare di aver agganciato un troll.
Tanti anni fa, per sapere se alcuni personaggi erano ancora in buona 
salute bastava scrivere cose del tipo:
 
int *a;
a = (int *)malloc(........)
 
ed ecco che saltava fuori il solito a riprenderti dicendo che il cast 
andava tolto (dal mio punto di vista non è necessario ma 'va tolto' è un 
altro discorso). Evidentemente quel seme continua a germogliare perché 
la gente, invece di leggere, interpreta.
In questo NG, negli anni, le ho provate tutte per schivare tali figuri:
ho provato a dar aiuto postando pezzi di codice d'ESEMPIO ripreso da 
cose scritte per CLI e mi han cazziato asserendo che non era C; allora 
ho deciso di proporre soluzioni in stile pascal-like come nei testi e 
son stato ancora cazziato perché non era C; scrivo esempi in C e mi 
viene contestata una if mentre si discute di algoritmi... vuoi sapere 
una cosa? son veramente stufo di leggere sproloqui pindarici di 
personaggi che confondono la programmazione con la filosofia e che si 
esprimono da opinionisti anziché dare spiegazioni serie (vedi il thread 
dell'extern).
Ancora una cosa: una ca77ata immane è quando facendo una rapina ammazzi 
qualcuno non quando non metti una if ;-)
 
Non preoccupatevi, non disturberò più.
 

 Non preoccupatevi, non disturberò più.
 
Sarebbe un peccato ;)
 
Ciao!
 
P.S. Avevo chiesto se questo nuovo google groups aveva ancora il problema nei reply e mi era stato detto di no: se così non fosse fatemelo presente!

 Non preoccupatevi, non disturberò più.
 
Non ho mai detto di non volerti leggere (cosa che tu hai detto di me) e 
mi dispiacerebbe che fossi io la causa di questo tuo gesto.
 
Detto questo, io mi fermo qui.