cURL einfach in C verwenden?



  • Ich hab mit C noch nie programmiert, außer Hallo Welt. Das was ich mal programmiert habe war Python und ist schon ne Weile her.

    Ich habe cURL und glaub auch libcurl runtergeladen und möchte diesen Befehl in C programmieren:

    curl -X GET "url"

    Es sollen Daten 1x von einem Server abgerufen werden. In der Kommandozeile funktioniert das auch.
    Ich muss mich dazu aber autorisieren und darum ist der Befehl:

    curl -H "Authorization: ..." -X GET "url"

    Leider lässt sich das nicht einfach in ein C Programm schreiben und da beginnt mein Problem.

    Ich habe diesen Code gefunden:

    #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, "http://example.com");
        /* example.com is redirected, so we tell libcurl to follow redirection */ 
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    
        /* 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);
      }
      return 0;
    }
    

    Geht das nicht einfacher? Ich steig leider schon bei diesem nicht wirklich durch. Am Anfang das Einbinden der Bibliotheken - ok.
    Die Hauptschleife - ok

    CURL *curl müsste eine Zeigervariable sein - hä, warum wieso weshalb, ich nix verstehen.

    CURLcode res - bitte was ist das? Gibt's irgendwo ne Möglichkeit das nachzuschlagen um es zu verstehen?

    dann curl_easy_init() ich hab auch keine Ahnung was das macht...

    Vielleicht kann jemand das Beispiel anpassen, oder vielleicht geht es einfacher, oder vielleicht gibt es Links wo die einzelnen Wörter, Befehle erklärt werden. Dann muss ich mich da halt mal durchbeißen.

    Ich nutze Codeblocks als IDE, meine Kommandozeile heißt Cygwin. Hallo Welt Programme kann ich kompilieren und ausführen, gcc ist eingerichtet, curl Bibliothek findet meine IDE auch.

    Gruß

    PS: Tolles Forum



  • qweet schrieb:

    curl -H "Authorization: ..." -X GET "url"

    Leider lässt sich das nicht einfach in ein C Programm schreiben und da beginnt mein Problem.

    Doch das lässt es:

    system("curl -H \"Authorization: ...\" -X GET \"url\"");
    

    Besser ist es hier aber statt der rabiaten system-Funktion die POSIX-Funktion popen zu verwenden, da damit die (Text)Rückgaben einfacher handhabbar sind.
    popen

    Das setzt natürlich voraus, dass du über ein fertiges curl-Compilat verfügst.



  • Wutz schrieb:

    Besser ist es hier aber statt der rabiaten system-Funktion die POSIX-Funktion popen zu verwenden, da damit die (Text)Rückgaben einfacher handhabbar sind.
    popen

    Das setzt natürlich voraus, dass du über ein fertiges curl-Compilat verfügst.

    Vielen Dank dir. Ich hab es mit popen ausprobiert und ich bin überrascht, es funktioniert.

    #include <stdio.h>
    #include <curl/curl.h>
    
      int main(void)
      {
    	/* char Ausgabe[] = ""; */
    
        popen("curl -H \"Authorization: xxx\" -X GET \"url\"","w");
    
        return 0;
      }
    

    Das Programm holt sich Daten vom Server und gibt diese in der Konsole aus.
    Es sieht so aus, als ob es Strings sind. Kann ich das in eine Variable umleiten?
    Darum hatte ich char Ausgabe[] definiert.

    Ja tut mir Leid, ich bin blutiger Anfänger. 😞


  • Mod

    popen funktioniert wie fopen. Man bekommt ein Dateihandle zurück, über das man dann lesend (über die ganz normalen Dateilesefunktionen, also fscanf, fread, usw.) auf die Ausgaben des geöffneten Prozesses zugreifen kann.



  • Und genau dafür steht ein Beispiel im Link.
    Willst du C programmieren, musst du sowieso mit fgets() usw. umgehen können.



  • Wutz schrieb:

    Und genau dafür steht ein Beispiel im Link.
    Willst du C programmieren, musst du sowieso mit fgets() usw. umgehen können.

    Ich hab mich mal an das Beispiel im Link gehalten:

    #include <stdio.h>
    #include <curl/curl.h>
    
      int main(void)
      {
    	FILE *fp;
        int status;
        char path[PATH_MAX];
    
        fp = popen("curl -H \"Authorization: \" -X GET \"url\"","w");
        /* Durch das Aufrufen von Curl werden die Daten in die Konsole geschrieben,
    das 1. Mal. */
    
        if (fp == NULL)
            /* Handle error */;
    
        while (fgets(path, PATH_MAX, fp) != NULL)
            printf("%s", path);
        /* Nach dieser Zeile erwarte ich, dass die Daten erneut ausgegeben werden. 
    Die Datei fp wurde in den String path eingelesen und müsste doch mit printf ausgegeben werden. */
    
        printf(path);
    /* ich hab diese Zeile eingefügt um halt nochmal händisch path auszugeben. Passiert nicht. */
    
        status = pclose(fp);
    
        if (status == -1) {
            /* Error reported by pclose() */
    
        } else {
        /* Use macros described under wait() to inspect `status' in order
           to determine success/failure of command executed by popen() */
    
        }
    
        // printf(path);
    
        return 0;
      }
    

    Wenn ich das Programm übersetze, werden die Daten einmal in die Konsole geschrieben. Ich erwarte aber, dass es 3 mal gemacht wird.
    Mit printf (path) scheint auch keine Ausgabe zu funktionieren.

    Letztenendes ist es doch egal, was mir popen für eine Datei zurückliefert, oder? Mit fgets wird daraus ein String und den müsste ich doch anzeigen lassen. Komplett oder Stückchenweise.



  • Du musst popen mit "r" statt "w" verwenden.



  • qweet schrieb:

    printf(path);
    

    Sowas sollte man sich nicht angewöhnen, denn der String könnte Format-Specifier enthalten (Stichwort: "format string vulnerability").



  • Wutz schrieb:

    Du musst popen mit "r" statt "w" verwenden.

    Da ist es ähnlich. Es wird zwar noch angezeigt, wie schnell die Daten gekommen sind und ob alle Daten abgerufen sind.

    Aber es wird nur 1x ausgegeben. Anscheinend ist nur

    while (fgets(path, PATH_MAX, fp) != NULL)
            printf("%s", path);
    

    dafür zuständig.

    ein

    printf("%s",path);
    

    macht leider nichts.

    Dann noch eine andere Frage: Ist es in Ordnung in diesem Forum/Thread auch nach der Einstellung von Codeblocks und libcurl zu fragen?
    Curl hab ich zwar die Verzeichnisse angegeben, aber ich muss noch den Linker einrichten.
    Angeblich ist libcurl bei curl mit dabei, ich find es aber nicht. Hab nur eine dll - Datei von libcurl.

    edit: Zwecks der ordentlichen Einrichtung von libcurl und Codeblocks werd ich wohl in einem anderen Forum fragen. Leider englisch. naja.



  • Du gehst offenbar davon aus, dass nach dem Durchlaufen der while-Schleife die eingelesen Daten komplett in 'path' zur Verfügung stehen und dann mit nur einem printf auch beliebig oft komplett ausgegeben werden können. Dem ist aber nicht so, im Normalfall (Schleife beendet wegen EOF) befindet sich dort dann der Inhalt der letzten erfolgreichen Leseoperation von fgets, oder, etwas vereinfacht, die letzte gelesene Zeile. Das könnte z.B. auch nur eine Leerzeile sein, demzufolge dein zusätzliches printf dann nur einen Zeilenvorschub ausgeben würde.


  • Mod

    Bezüglich Compiler&IDE-Fragen: Dafür gibt es hier ein anderes Unterforum. Bitte solche Fragen nicht im C-Forum stellen.



  • printf(path) schrieb:

    Das könnte z.B. auch nur eine Leerzeile sein, demzufolge dein zusätzliches printf dann nur einen Zeilenvorschub ausgeben würde.

    Sowas kannst du mit Begrenzern vor und nach dem String feststellen.

    printf("<%s>",path);
    

    Wenn dann in der Konsole nur <> steht, war es ein Leerstring.
    Bei
    <

    ist zumindest ein Zeilenvorschub drin.

    Oder du nimmst den Debugger.



  • printf(path) schrieb:

    Du gehst offenbar davon aus, dass nach dem Durchlaufen der while-Schleife die eingelesen Daten komplett in 'path' zur Verfügung stehen und dann mit nur einem printf auch beliebig oft komplett ausgegeben werden können. Dem ist aber nicht so, [...]

    Ja stimmt, davon bin ich ausgegangen. Ich möchte die Daten doch dann dauerhaft im Programm verwenden. Brauch ich vielleicht einen anderen Befehl als fgets?

    Oder muss ich jedesmal die while Schleife haben um die Datei zu lesen? Ich denke, das geht auch, find ich aber nicht sehr schön.

    Ich dachte es funktioniert so:

    1. Daten vom Server holen und in Datei schieben
    2. Datei in String übergeben
    3. Beliebig oft auf diesen String zugreifen. Einzelne oder alle Zeichen.



  • Entweder du weist curl an,

    - die gewünschten Daten gleich eine Datei zu speichern: dann musst du die Datei anschließend ständig (zeilenweise) einlesen oder einmal in eine Stringliste einlesen aus der du dich dann beliebig oft bedienen kannst
    - du lässt curl die Daten auf stdout ausgeben: dann kannst du via popen+fgets+pclose gleich in eine Stringliste einlesen (ohne Zwischendatei) und diese Stringliste im Programm beliebig verarbeiten


  • Mod

    qweet schrieb:

    Ja stimmt, davon bin ich ausgegangen. Ich möchte die Daten doch dann dauerhaft im Programm verwenden. Brauch ich vielleicht einen anderen Befehl als fgets?

    popen gibt dir, wie schon erwähnt, einen ganz normalen Dateistream (außer dass Hin- und Herspringen wahrscheinlich nicht funktioniert). Den kannst du auslesen, wie es dir gefällt und wie auch immer du die Daten hinterher im Programm haben musst. Das muss also nicht durch fgets geschehen. Man muss aber natürlich die üblichen Lesefunktionen kennen und was wissen, wie man die Daten in eine gewünschte Form bringt. Ein bisschen Erfahrung ist also schon nötig.

    Du solltest dich von der Vorstellung trennen, dass das irgendwelche geheimnisvolle Magie wäre. Würdest du mit dem Einlesen zurecht kommen, wenn jemand die curl-Ausgabe ausdrucken und von Hand in dein Programm eintippen würde? Oder wenn die Daten als Datei gespeichert wären? Falls ja, dann gibt es überhaupt kein Problem, denn dann erfolgt das Lesen des popen-Streams einfach exakt genau so, wie du es bei der manuellen Eingabe oder bei der Datei machen würdest. Falls nein, dann hast du ein anderes Problem, nämlich dass du nicht genug Übung in C hast, um einfache Ein-/Ausgabeoperationen selber zu programmieren.



  • SeppJ schrieb:

    [...] Man muss aber natürlich die üblichen Lesefunktionen kennen und was wissen, wie man die Daten in eine gewünschte Form bringt. Ein bisschen Erfahrung ist also schon nötig.

    Da muss ich passen, kenne mich überhaupt nicht aus.

    SeppJ schrieb:

    [...]nämlich dass du nicht genug Übung in C hast, um einfache Ein-/Ausgabeoperationen selber zu programmieren.

    Oje, ich hab überhaupt keine Übung in C. Kennst du ein gutes deutsches Buch als Einstieg? Ich suche aber eins wo gut erklärt wird, wie man Bibliotheken einbindet.

    Vielleicht sollte ich mir die Programmierleistung auch kaufen. Ich möchte aber gern den Quellcode haben. Kennt ihr da vielleicht was? Ich hab jetzt twago.de gefunden.

    Mit Curl kommen die Daten in stdout. Ich kann sie auch in eine Datei schicken, leider nur in einem speziellen Verzeichnis.
    Wenn ich

    popen("curl -H \"Authorization: \" -X GET \"url\" -o C:\Verzeichnis\fdsa.txt","r");
    

    wird die Datei nicht erstellt.



  • Der Backslash leitet eine Esacapesequenz ein.
    Den musst du genauso behandeln wie Anführungszeichen im Stringliteral.

    Denn ein Seitenvorschub '\f' kommt im Dateinamen nicht so gut.

    Aber das gilt nicht nur für C sondern in vielen Programmiersprachen.



  • DirkB schrieb:

    Der Backslash leitet eine Esacapesequenz ein.
    Den musst du genauso behandeln wie Anführungszeichen im Stringliteral.[...]

    Und wie mach ich das?

    Meine einzige Referenz ist z.Z. https://de.wikibooks.org/wiki/C-Programmierung

    Ich hab es versucht mit zwei \
    also C:\\Verzeichnis\\

    funktioniert aber auch nicht.



  • qweet schrieb:

    Und wie mach ich das?

    Meine einzige Referenz ist z.Z. https://de.wikibooks.org/wiki/C-Programmierung

    Steht doch drin: https://de.wikibooks.org/wiki/C-Programmierung:_Variablen_und_Konstanten#Kodierung_von_Zeichenketten

    qweet schrieb:

    Ich hab es versucht mit zwei \
    also C:\\Verzeichnis\\

    funktioniert aber auch nicht.

    Hast du wirklich Verzeichnis da stehen oder etwas anderes?



  • Ich wollte euch mal meine Lösung vorstellen:

    int main(void) {
        FILE *fp;
        int i=0, j=0;
        int c;
        int string[PATH_MAX];
    
        fp = popen("curl -H \"Authorization: \" -X GET \"url\"","r");
    
        if (fp == NULL) {
            printf("Konnte Datei nicht öffnen!\n");
        }
    
        while( (c=fgetc(fp)) != EOF) {
             string[i] = c;
             i++;
        }
        fclose (fp);
        j = i;
    
        for(i=0; i<=j; i++) {
            putchar(string[i]);
        //    printf ("%i",string[i]);
        }
    
        return 0;
    }
    

    Ich lese die Datei zeichenweise in einen Array ein um sie dort später wiederzuverwenden.
    Die Länge des Arrays wird in j gespeichert.

    Nun kann ich auf meinen Array zugreifen, diesen ausgeben oder anderweitig im Programm verwenden.
    Ich denke es sollte kein Problem sein diesen in einen char[] umzuwandeln.

    Vielleicht könnt ihr noch schreiben, was ihr davon haltet (gut/schlecht).
    Vielleicht könnt ihr was zu PATH_MAX sagen. Ich weiß nicht was das bedeutet, hab es nur drin weil ich es aus einem Beispiel kopiert habe.


Log in to reply