Создание класса C++ для чтения данных с веб-сайта в строку с использованием libcurl

Я использую http://ubuntuforums.org/showthread.php?t=781021 в качестве руководство о том, как написать программу на С++, которая использует libcurl для проверки веб-сайта и загрузки текста в приложение на С++. Когда я копирую код в свой проект визуальной студии, он работает с небольшими изменениями. Однако я столкнулся с проблемой, пытаясь преобразовать его в файлы на основе классов.

Я создал файлы .h и .cpp для выполнения запроса. Чего я раньше не осознавал, так это того, что handle_data не является типичной функцией, поскольку ей не передаются никакие аргументы или даже () в середине Viewer.cpp. Вот как это сделал вышеупомянутый сайт, и это сработало, но я не понимаю, почему и как его преобразовать сейчас.

Кто-нибудь знает, что здесь происходит или как это исправить? Спасибо!

Зритель.h:

#pragma once

#include <string>
#include "curl.h"

class Viewer
{
public:
    Viewer(void);
    ~Viewer(void);

    std::string view(std::string q);

private:
    size_t handle_data(void *ptr, size_t size, size_t nmemb, void *stream);

    std::string contents;

};

Средство просмотра.cpp:

#include "stdafx.h"
#include "Viewer.h"
#include <iostream>

Viewer::Viewer(void)
{
std::cout << "ViewerCreated!\n";
}


Viewer::~Viewer(void)
{
}

size_t Viewer::handle_data(void *ptr, size_t size, size_t nmemb, void *stream) 
{ 
    int numbytes = size*nmemb; 
    // The data is not null-terminated, so get the last character, and replace 
    // it with '\0'. 
    char lastchar = *((char *) ptr + numbytes - 1); 
    *((char *) ptr + numbytes - 1) = '\0'; 
    contents.append((char *)ptr); 
    contents.append(1,lastchar); 
    *((char *) ptr + numbytes - 1) = lastchar;  // Might not be necessary. 
    return size*nmemb; 
}

std::string Viewer::view(std::string q)
{
    char* url = "www.google.com";

    CURL* curl = curl_easy_init(); 
    if(curl) 
        { 
        // Tell libcurl the URL 
        curl_easy_setopt(curl,CURLOPT_URL, url); 
        // Tell libcurl what function to call when it has data 
        curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,handle_data); 
        // Do it! 
        CURLcode res = curl_easy_perform(curl); 
        curl_easy_cleanup(curl); 
        if (res == 0) 
            std::cout << contents << std::endl; 
        else 
            std::cerr << "Error: " << res << std::endl; 
        } 

    return contents;
}

person user3175137    schedule 27.02.2014    source источник
comment
handle_data должен быть статичным. Один пример того, как это сделать можно найти здесь   -  person WhozCraig    schedule 28.02.2014


Ответы (3)


Вы не должны заменять последний символ чанка, который вы получили. Вместо этого вы должны заставить буфер, содержащий добавленные блоки, иметь '\ 0' сразу после конца данных. Таким образом, его можно распечатать, но если вы собираетесь хранить его в файле, вам следует просто записать полученные байты.

Это всегда работало для меня:

struct MemoryStruct chunk;
chunk.memory=NULL;
chunk.size = 0; 
...
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

И обратный вызов:

struct MemoryStruct {
   char *memory;
   size_t size;
};

static void *myrealloc(void *ptr, size_t size)
{
   if(ptr)
      return realloc(ptr, size);
    else
      return malloc(size);
 }

static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
   size_t realsize = size * nmemb;
   struct MemoryStruct *mem = (struct MemoryStruct *)data;
   mem->memory = (char *)myrealloc(mem->memory, mem->size + realsize + 1);
   if (mem->memory) {
      memcpy(&(mem->memory[mem->size]), ptr, realsize);
      mem->size += realsize;
      mem->memory[mem->size] = 0;
   }
   return realsize;
}
person jsanzmarcos    schedule 27.02.2014
comment
Это именно то, что мне было нужно, спасибо. Оно работало завораживающе! - person user3175137; 28.02.2014

handle_data используется как параметр для curl_easy_setopt. Это означает, что вы должны запрограммировать, что делает handle_data, когда он получает следующий блок данных (при условии, что блок данных — это то, что curl сделает за вас).

person Ashalynd    schedule 27.02.2014
comment
Хорошо. Так это функция, которая требует заголовка Viewer::? или у меня есть другой способ создать его, чтобы header_data знал, что и когда делать? Я думаю, что мне не хватает ключевой функциональности header_data... - person user3175137; 28.02.2014
comment
Насколько я понимаю, не совсем. Я предполагаю, что curl_easy_setopt примет любую функцию с такой же сигнатурой (тип возвращаемого значения + аргументы). - person Ashalynd; 28.02.2014

handle_data передается curl_easy_setopt в качестве функции обратного вызова, которую он вызовет, когда будут доступны данные. Из-за этого handle_data должна вызываться как статическая функция (то есть функция, не получающая указатель объекта, потому что cURL не имеет его). Однако вы не объявляете его статическим. Даже если вы это сделаете, вы получите доступ к contents внутри него, что недопустимо.

person Pedro Silva    schedule 27.02.2014