2 votes

L'ajout à un tableau de caractères ne fonctionne pas

J'essaie de lire un fichier texte ligne par ligne, et d'ajouter chaque ligne à un tableau de caractères. Mais les lignes ne sont pas ajoutées, pas du tout.

//This is the default char array that comes with the cURL code.
char *text[]={
  "one\n",
  "two\n",
  "three\n",
  " Hello, this is CURL email SMTP\n",
  NULL
};

/*Now we're going to replace that char array, with an array that holds the contents of a textfile. 
  We'll read a textfile out line by line, and add each line to the char array. 
*/
void makemailmessage()
{
    text[0] = '\0'; //Clear text
    text[0] = "testy\n"; //First line in new char array

    //Read the text file, add each line to the char array.
    string line;
    ifstream myfile ("C:\\Users\\admin\\Downloads\\bbb.txt");
    int counter;
    counter = 1;
    if (myfile.is_open())
    {
        while ( myfile.good() )
        {
          getline (myfile,line);

            //Convert the string variable "line" to a char (a)
            char *a=new char[line.size()+1];
            a[line.size()]=0;
            memcpy(a,line.c_str(),line.size());

            //Add \n to the end of "a" (new char will be "str")
            char str[80];
            strcpy (str,a);
            strcat (str,"\n");

            //Add "str" to the char array "text"
            text[counter] = str;
            text[counter+1] = "test\n"; //Also added this for testing purposes

            write_data("C:\\Users\\admin\\Downloads\\checkit.txt", str); //Also for testing purposes

        //Increase counter by 2 because we added two new items to the char array "text"
        counter++;
        counter++;
        }
    myfile.close();

    text[counter-1] = "testy2\n"; //Ad another text line
    text[counter] = NULL; //End char array
}

Chaque chaîne est écrite correctement dans le fichier checkit.txt mais, pour une raison quelconque, elle n'est pas ajoutée au tableau de chars, ce qui fait que le tableau de chars ressemble à ceci :

testy

test

test

testy2

Qu'est-ce que je fais de mal ?

UPDATE2 : La raison pour laquelle j'essaie de créer un tableau de caractères est que la fonction cURL que j'utilise a besoin d'un tableau de caractères pour former le corps de l'e-mail. C'est la partie importante du code cURL.

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
  struct WriteThis *pooh = (struct WriteThis *)userp;
  const char *data;

  if(size*nmemb < 1)
    return 0;

  data = text[pooh->counter]; //This part is using the char array.

  if(data) {
    size_t len = strlen(data);
    memcpy(ptr, data, len);
    pooh->counter++;
    return len;
  }
  return 0;
}

Voici le code complet

2voto

AusCBloke Points 10179

J'essaie de lire un fichier texte ligne par ligne, et d'ajouter chaque ligne à un fichier tableau de chars.

Puisqu'il s'agit de C++, pourquoi ne pas utiliser un fichier de type std::vector<string> et utiliser le version std::string de getline ?

El std::string s'occupera de la mémoire nécessaire pour contenir une chaîne de caractères de n'importe quelle longueur, et la classe std::vector se préoccupe de la mémoire nécessaire pour contenir un "tableau", pour ainsi dire, de chaînes de caractères.

EDITAR: En fait, en regardant à nouveau votre code, vous utilisez bien un fichier std::string et ensuite allouer de la mémoire pour le stocker comme un tableau de char s, et ensuite stocker les pointeurs vers ces chaînes dans un tableau de taille fixe, test . Pourquoi se donner tout ce mal alors que, comme je l'ai mentionné plus haut, vous pouvez utiliser une std::vector<string> pour contenir tous vos std::string objets ? L'esprit = perplexe.

EDIT2 : Ne pourriez-vous pas également utiliser cURLpp comme une enveloppe C++ pour cURL ? Je n'ai utilisé ni l'un ni l'autre, je ne peux donc pas me prononcer sur leur efficacité.

2voto

sehe Points 123151

Ok, après avoir discuté de ça un peu plus, voici une solution :

Version C++

Fichier de code complet ici : https://gist.github.com/1342118#file_test.cpp

Remplacez le code correspondant par :

#include <vector>
#include <fstream>

// ...

std::vector<std::string> text;

static int read_text(char* fname)
{
    //Read the text file, add each line to the char array.
    std::ifstream myfile (fname);

    std::string line;
    while (std::getline(myfile, line))
        text.push_back(line + '\n');

    return 0;
}

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
    /* This was already in. */
  struct WriteThis *pooh = (struct WriteThis *)userp;

  if(size*nmemb < 1)
    return 0;

  if (pooh->counter < text.size())
  {
      const std::string& data = text[pooh->counter];

      memcpy(ptr, data.data(), data.length());
      pooh->counter++; /* advance pointer */
      return data.length();
  }
  return 0;                         /* no more data left to deliver */
}

Version C pure

Fichier de code complet ici : https://gist.github.com/1342118#file_test.c

Remplacer

//This is the default char array that comes with the cURL code.
char *text[]={
  "one\n",
  "two\n",
  "three\n",
  " Hello, this is CURL email SMTP\n",
  NULL
};

Avec

char **text = 0;

static int read_text(char* fname)
{
    unsigned capacity = 10;
    int linecount = 0;

    // free_text(); see below
    text = realloc(text, capacity*sizeof(*text));

    FILE* file = fopen(fname, "r");
    if (!file)
        { perror("Opening file"); return 1; }

    char buf[2048];
    char* line = 0;

    while (line = fgets(buf, sizeof(buf), file))
    {
        if (linecount>=capacity)
        {
            capacity *= 2;
            text = realloc(text, capacity*sizeof(*text));
        }
        text[linecount++] = strdup(line);
    } 

    fclose(file);

    return 0;
}

Accrochez-le en vous main par exemple, comme suit

if (argc<2)
{
    printf("Usage: %s <email.eml>\n", argv[0]);
    exit(255);
} else
{
    printf("Reading email body from %s\n", argv[1]);
    if (0 != read_text(argv[1]))
        exit(254);
}

Ou, si vous préférez, appelez simplement read_text("C:\\Users\\admin\\Downloads\\bbb.txt") :)

Pour couronner le tout, n'oubliez pas de récupérer la mémoire lorsque vous aurez terminé - correctement :

#include "curl/curl.h" 

#include <stdio.h> 
#include <stdlib.h> 

#include <unistd.h> 
#include <memory.h>
#include <string.h>
#define GetCurrentDir getcwd 

#define USERNAME "obscured@gmail.com"
#define PASSWORD "obscured"
#define SMTPSERVER "smtp.gmail.com"
#define SMTPPORT ":587"
#define RECIPIENT "<obscured@gmail.com>"
#define MAILFROM "<obscured@gmail.com>"

#define MULTI_PERFORM_HANG_TIMEOUT 60 * 1000

/* Note that you should include the actual meta data headers here as well if
   you want the mail to have a Subject, another From:, show a To: or whatever
   you think your mail should feature! */
char **text = 0;

void free_text()
{
    if (text)
    {
        char** it;
        for (it = text; *it; ++it)
            free(*it);
        free(text);
        text = 0;
    }
}

static int read_text(char* fname)
{
    unsigned capacity = 10;
    int linecount = 0;

    free_text();
    text = realloc(text, capacity*sizeof(*text));

    FILE* file = fopen(fname, "r");
    if (!file)
        { perror("Opening file"); return 1; }

    char buf[2048];
    char* line = 0;

    while (line = fgets(buf, sizeof(buf), file))
    {
        if (linecount>=capacity)
        {
            capacity *= 2;
            text = realloc(text, capacity*sizeof(*text));
        }
        text[linecount++] = strdup(line);
    } 

    if (linecount>=capacity)
        text = realloc(text, (++capacity)*sizeof(*text));

    text[linecount] = 0; // terminate

    fclose(file);

    return 0;
}

struct WriteThis {
  int counter;
};

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
    /* This was already in. */
  struct WriteThis *pooh = (struct WriteThis *)userp;
  const char *data;

  if(size*nmemb < 1)
    return 0;

  data = text[pooh->counter];

  if(data) {
    size_t len = strlen(data);
    memcpy(ptr, data, len);
    pooh->counter++; /* advance pointer */
    return len;
  }
  return 0;                         /* no more data left to deliver */
}

static struct timeval tvnow(void)
{
  /*
  ** time() returns the value of time in seconds since the Epoch.
  */
  struct timeval now;
  now.tv_sec = (long)time(NULL);
  now.tv_usec = 0;
  return now;
}

static long tvdiff(struct timeval newer, struct timeval older)
{
  return (newer.tv_sec-older.tv_sec)*1000+
    (newer.tv_usec-older.tv_usec)/1000;
}

int main(int argc, char** argv)
{
    if (argc<2)
    {
        printf("Usage: %s <email.eml>\n", argv[0]);
        exit(255);
    } else
    {
        printf("Reading email body from %s\n", argv[1]);
        if (0 != read_text(argv[1]))
            exit(254);
    }

   CURL *curl;
   CURLM *mcurl;
   int still_running = 1;
   struct timeval mp_start;
   char mp_timedout = 0;
   struct WriteThis pooh;
   struct curl_slist* rcpt_list = NULL;

   pooh.counter = 0;

   curl_global_init(CURL_GLOBAL_DEFAULT);

   curl = curl_easy_init();
   if(!curl)
     return 1;

   mcurl = curl_multi_init();
   if(!mcurl)
     return 2;

   rcpt_list = curl_slist_append(rcpt_list, RECIPIENT);
   /* more addresses can be added here
      rcpt_list = curl_slist_append(rcpt_list, "<others@example.com>");
   */

   curl_easy_setopt(curl, CURLOPT_URL, "smtp://" SMTPSERVER SMTPPORT);
   curl_easy_setopt(curl, CURLOPT_USERNAME, USERNAME);
   curl_easy_setopt(curl, CURLOPT_PASSWORD, PASSWORD);
   curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
   curl_easy_setopt(curl, CURLOPT_MAIL_FROM, MAILFROM);
   curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt_list);
   curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
   curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,0);
   curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
   curl_easy_setopt(curl, CURLOPT_READDATA, &pooh);
   curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
   curl_easy_setopt(curl, CURLOPT_SSLVERSION, 0);
   curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0);
   curl_multi_add_handle(mcurl, curl);

   mp_timedout = 0;
   mp_start = tvnow();

  /* we start some action by calling perform right away */
  curl_multi_perform(mcurl, &still_running);

  while(still_running) {
    struct timeval timeout;
    int rc; /* select() return code */

    fd_set fdread;
    fd_set fdwrite;
    fd_set fdexcep;
    int maxfd = -1;

    long curl_timeo = -1;

    FD_ZERO(&fdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&fdexcep);

    /* set a suitable timeout to play around with */
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    curl_multi_timeout(mcurl, &curl_timeo);
    if(curl_timeo >= 0) {
      timeout.tv_sec = curl_timeo / 1000;
      if(timeout.tv_sec > 1)
        timeout.tv_sec = 1;
      else
        timeout.tv_usec = (curl_timeo % 1000) * 1000;
    }

    /* get file descriptors from the transfers */
    curl_multi_fdset(mcurl, &fdread, &fdwrite, &fdexcep, &maxfd);

    /* In a real-world program you OF COURSE check the return code of the
       function calls.  On success, the value of maxfd is guaranteed to be
       greater or equal than -1.  We call select(maxfd + 1, ...), specially in
       case of (maxfd == -1), we call select(0, ...), which is basically equal
       to sleep. */

    //rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

    if (tvdiff(tvnow(), mp_start) > MULTI_PERFORM_HANG_TIMEOUT) {
      fprintf(stderr, "ABORTING TEST, since it seems "
              "that it would have run forever.\n");
      break;
    }

    switch(rc) {
    case -1:
      /* select error */
      break;
    case 0: /* timeout */
    default: /* action */
      curl_multi_perform(mcurl, &still_running);
      break;
    }
  }

  curl_slist_free_all(rcpt_list);
  curl_multi_remove_handle(mcurl, curl);
  curl_multi_cleanup(mcurl);
  curl_easy_cleanup(curl);
  curl_global_cleanup();
  free_text();
  return 0;
}

1voto

thkala Points 36148

What am I doing wrong?

Pour commencer, ceci :

    char str[80];
    strcpy (str,a);
    strcat (str,"\n");

    //Add "str" to the char array "text"
    text[counter] = str;

str est alloué sur la pile, avec une portée à l'échelle du bloc. Ensuite, vous entrez ce pointeur dans un tableau avec une portée plus grande. C'est généralement la recette d'un désastre - un défaut de segmentation plutôt impressionnant ou l'équivalent sur votre plate-forme.

Dans ce cas, en raison de son utilisation dans une boucle, votre programme va soit se planter, soit - si les étoiles sont correctement alignées - vous vous retrouverez avec tous les pointeurs de votre tableau pointant sur la même chaîne hors champ, à savoir celle qui a été lue en dernier.

Pourquoi se donner cette peine, alors que vous avez déjà alloué dynamiquement a dans le tas ?

Au fait, le mélange char[] (et les fonctions associées de la bibliothèque C standard) avec des chaînes de caractères C++ n'est PAS une bonne idée. Même pas une idée acceptable. OK, c'est une mauvais idée. Contentez-vous des chaînes C++...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X