C libcurl obtém a saída em uma string

94

Desejo armazenar o resultado desta função curl em uma variável, como posso fazer isso?

#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;
}

obrigado, resolvi assim:

#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;
}
frx08
fonte
1
Apenas para apontar em sua solução em function_pt (), você está convertendo a string em ptr para inteiro a fim de convertê-la de volta em string na saída. Você pode enviar a string diretamente (e ver a resposta completa).
zzz
2
aqui está um link para o exemplo cURL curl.haxx.se/libcurl/c/getinmemory.html
lafferc
1
CURLcode res;não é usado
fnc12,
mesma pergunta, mas para C ++ em vez de c vá para aqui: Salve o resultado do conteúdo cURL em uma string em C ++
Trevor Boyd Smith

Respostas:

114

Você pode definir uma função de retorno de chamada para receber blocos de dados de entrada usando curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, myfunc);

O retorno de chamada terá um argumento definido pelo usuário que você pode definir usando curl_easy_setopt(curl, CURLOPT_WRITEDATA, p)

Aqui está um trecho de código que passa um buffer struct string {*ptr; len}para a função de retorno de chamada e aumenta esse buffer em cada chamada usando 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;
}
Alex Jasmin
fonte
1
Agradável. Ainda melhor se todos aqueles size_t(além de lensi mesmo) fossem declarados const.
alk
1
para C ++ std::stringaqui
Trevor Boyd Smith
33

A seguinte resposta é a maneira C ++ de fazer isso, com std::string, em vez de uma string terminada em nulo. Ele ainda usa uma função de retorno de chamada (não há como contornar isso), mas também lida com erros de alocação usando 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;
}
The Quantum Physicist
fonte
Acho que std :: string :: append pode tornar essa função de retorno de chamada muito mais simples.
rnickb
@rnickb Você está certo; s->append((char*)contents. nmemb);funciona perfeitamente comigo e é mais conciso. Além disso, a assinatura da função oficial para o retorno de chamada tem um char*primeiro argumento, portanto, você pode usá-lo e omitir a conversão. E, por último, s->resize()inicializa de fato o espaço recém-alocado. Como você está prestes a sobrescrevê-lo de qualquer maneira, s->reserve()seria mais eficiente.
Jeinzi
Isso me ajudou muito. Você também pode dar um exemplo de como fazer isso com HTTP POST, por favor :-)
Lord Wolfenstein
9

Lendo o manual aqui: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html Acho que você precisa de várias chamadas para CURL_SETOPT, a primeira sendo o URL que deseja processar, a segunda sendo algo como:

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_ptr);

Onde function_ptr corresponde a esta assinatura:

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

O que acontece aqui é que você denota uma função de retorno de chamada que libcurl chamará quando tiver alguma saída para escrever de qualquer transferência que você tenha invocado. Você pode fazer com que ele grave automaticamente em um arquivo ou passe um ponteiro para uma função que tratará a própria saída. Usando esta função, você deve ser capaz de montar as várias strings de saída em uma peça e depois usá-las em seu programa.

Não tenho certeza de quais outras opções você pode ter para definir / o que mais afeta como você deseja que seu aplicativo se comporte, então dê uma boa olhada nessa página.


fonte
0

Aqui está um sabor C ++ da resposta aceita de 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;
}
Paul Grinberg
fonte