C libcurl obtient la sortie dans une chaîne


94

Je souhaite stocker le résultat de cette fonction curl dans une variable, comment puis-je le faire?

#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
  CURL *curl;
  CURLcode res;

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    res = curl_easy_perform(curl);

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

merci, je l'ai résolu comme ceci:

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

function_pt(void *ptr, size_t size, size_t nmemb, void *stream){
    printf("%d", atoi(ptr));
}

int main(void)
{
  CURL *curl;
  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_pt);
    curl_easy_perform(curl);
    curl_easy_cleanup(curl);
  }
  system("pause");
  return 0;
}

1
Juste pour souligner dans votre solution dans function_pt () que vous convertissez la chaîne de ptr en entier afin de la reconvertir en chaîne dans la sortie. Vous pouvez afficher la chaîne directement (et voir la réponse complète).
zzz

2
voici un lien vers l'exemple cURL curl.haxx.se/libcurl/c/getinmemory.html
lafferc

1
CURLcode res;n'est pas utilisé
fnc12

même question mais pour C ++ au lieu de c, allez ici: Enregistrer le résultat du contenu cURL dans une chaîne en C ++
Trevor Boyd Smith

Réponses:


114

Vous pouvez définir une fonction de rappel pour recevoir des blocs de données entrants en utilisant curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, myfunc);

Le rappel prendra un argument défini par l'utilisateur que vous pouvez définir en utilisant curl_easy_setopt(curl, CURLOPT_WRITEDATA, p)

Voici un extrait de code qui passe un tampon struct string {*ptr; len}à la fonction de rappel et augmente ce tampon à chaque appel à l'aide de realloc ().

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

struct string {
  char *ptr;
  size_t len;
};

void init_string(struct string *s) {
  s->len = 0;
  s->ptr = malloc(s->len+1);
  if (s->ptr == NULL) {
    fprintf(stderr, "malloc() failed\n");
    exit(EXIT_FAILURE);
  }
  s->ptr[0] = '\0';
}

size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s)
{
  size_t new_len = s->len + size*nmemb;
  s->ptr = realloc(s->ptr, new_len+1);
  if (s->ptr == NULL) {
    fprintf(stderr, "realloc() failed\n");
    exit(EXIT_FAILURE);
  }
  memcpy(s->ptr+s->len, ptr, size*nmemb);
  s->ptr[new_len] = '\0';
  s->len = new_len;

  return size*nmemb;
}

int main(void)
{
  CURL *curl;
  CURLcode res;

  curl = curl_easy_init();
  if(curl) {
    struct string s;
    init_string(&s);

    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
    res = curl_easy_perform(curl);

    printf("%s\n", s.ptr);
    free(s.ptr);

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

1
Agréable. Encore plus agréable si tout cela size_t(à part lenlui-même) était déclaré const.
alk

1
pour C ++, std::stringallez ici
Trevor Boyd Smith

33

La réponse suivante est la façon C ++ de le faire, avec std::string, au lieu d'une chaîne terminée par un caractère nul. Il utilise toujours une fonction de rappel (il n'y a aucun moyen de contourner cela), mais gère également les erreurs d'allocation en utilisant try / catch.

#include <iostream>
#include <string>
#include <curl/curl.h>

size_t CurlWrite_CallbackFunc_StdString(void *contents, size_t size, size_t nmemb, std::string *s)
{
    size_t newLength = size*nmemb;
    try
    {
        s->append((char*)contents, newLength);
    }
    catch(std::bad_alloc &e)
    {
        //handle memory problem
        return 0;
    }
    return newLength;
}
int main()
{
    CURL *curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_DEFAULT);

    curl = curl_easy_init();
    std::string s;
    if(curl)
    {

        curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");

        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //only for https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); //only for https
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
        curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); //remove this to disable verbose output


        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if(res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));
        }

        /* always cleanup */
        curl_easy_cleanup(curl);
    }

    std::cout<<s<<std::endl;

    std::cout<< "Program finished!" << std::endl;
}

Je pense que std :: string :: append peut rendre cette fonction de rappel beaucoup plus simple.
rnickb

@rnickb Vous avez raison; s->append((char*)contents. nmemb);fonctionne parfaitement avec moi et est plus concis. En outre, la signature de fonction officielle pour le rappel a un char*comme premier argument, vous pouvez donc l'utiliser et omettre le casting. Et enfin, s->resize()initialise réellement l'espace nouvellement alloué. Comme vous êtes sur le point de l'écraser de toute façon, ce s->reserve()serait plus efficace.
Jeinzi

Cela m'a beaucoup aidé. Pouvez-vous également donner un exemple de la façon de le faire avec HTTP POST s'il vous plaît :-)
Lord Wolfenstein

9

En lisant le manuel ici: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html Je pense que vous avez besoin de plusieurs appels à CURL_SETOPT, le premier étant l'URL que vous souhaitez traiter, le second étant quelque chose comme:

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_ptr);

Où function_ptr correspond à cette signature:

size_t function( void *ptr, size_t size, size_t nmemb, void *stream)

Ce qui se passe ici, c'est que vous dénotez une fonction de rappel que libcurl appellera quand elle aura une sortie à écrire à partir du transfert que vous avez appelé. Vous pouvez lui faire écrire automatiquement dans un fichier ou lui passer un pointeur vers une fonction qui gérera la sortie elle-même. En utilisant cette fonction, vous devriez être capable d'assembler les différentes chaînes de sortie en un seul morceau, puis de les utiliser dans votre programme.

Je ne suis pas sûr des autres options que vous pourriez avoir à définir / quoi d'autre affecte le comportement de votre application, alors jetez un œil à cette page.


0

Voici une version C ++ de la réponse acceptée par alex-jasmin

#include <iostream>
#include <string>
#include <curl/curl.h>

size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) 
{
  s->append(static_cast<char *>(ptr), size*nmemb);
  return size*nmemb;
}

int main(void)
{
  CURL *curl = curl_easy_init();
  if (curl)
  {
    std::string s;

    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);

    CURLcode res = curl_easy_perform(curl);

    std::cout << s << std::endl;

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.