Gibt es alternative Implementierungen der GNU-Getline-Schnittstelle?

Lesezeit: 7 Minuten

dmckee --- Benutzeravatar des Ex-Moderators Kitten
dmckee — Ex-Moderator-Kätzchen

Das Experiment, an dem ich gerade arbeite, verwendet eine Softwarebasis mit einer komplizierten Quellhistorie und ohne klar definierte Lizenz. Es wäre ein erheblicher Arbeitsaufwand, die Dinge zu rationalisieren und unter einer festen Lizenz zu veröffentlichen.

Es soll auch eine zufällige unixische Plattform ausführen, und nur einige der von uns unterstützten libcs ​​haben GNU getline, aber im Moment erwartet der Code dies.

Kennt jemand eine Neuimplementierung der GNU getline Semantik, die unter einer weniger restriktiven Lizenz verfügbar ist?

Bearbeiten:: Ich frage, weil Google nicht geholfen hat und ich es nach Möglichkeit vermeiden möchte, eine zu schreiben (es könnte eine lustige Übung sein, aber es kann nicht die beste Nutzung meiner Zeit sein).

Genauer gesagt handelt es sich um folgende Schnittstelle:

ssize_t getline (char **lineptr, size_t *n, FILE *stream);

  • Aufgrund dieser Frage habe ich die Erklärung korrigiert; getline kehrt zurück ssize_tnicht size_t.

    – Keith Thompson

    1. Juli 2013 um 15:07 Uhr

  • Eine gemeinfreie Implementierung von getline(): stackoverflow.com/a/12169132/12711

    – Michael Burr

    2. Oktober 2013 um 19:41 Uhr

  • Würden Sie eine andere Antwort auf diese Frage akzeptieren?

    – Antti Haapala – Слава Україні

    19. Mai 2019 um 11:37 Uhr


Ich bin verwirrt.

Ich habe mir den Link angesehen, die Beschreibung gelesen, und das ist ein gutes Dienstprogramm.

Aber wollen Sie damit sagen, dass Sie diese Funktion einfach nicht nach Spezifikation umschreiben können? Die Spezifikation scheint ziemlich klar zu sein,

Hier:

/* This code is public domain -- Will Hartung 4/9/09 */
#include <stdio.h>
#include <stdlib.h>

size_t getline(char **lineptr, size_t *n, FILE *stream) {
    char *bufptr = NULL;
    char *p = bufptr;
    size_t size;
    int c;

    if (lineptr == NULL) {
        return -1;
    }
    if (stream == NULL) {
        return -1;
    }
    if (n == NULL) {
        return -1;
    }
    bufptr = *lineptr;
    size = *n;

    c = fgetc(stream);
    if (c == EOF) {
        return -1;
    }
    if (bufptr == NULL) {
        bufptr = malloc(128);
        if (bufptr == NULL) {
            return -1;
        }
        size = 128;
    }
    p = bufptr;
    while(c != EOF) {
        if ((p - bufptr) > (size - 1)) {
            size = size + 128;
            bufptr = realloc(bufptr, size);
            if (bufptr == NULL) {
                return -1;
            }
        }
        *p++ = c;
        if (c == '\n') {
            break;
        }
        c = fgetc(stream);
    }

    *p++ = '\0';
    *lineptr = bufptr;
    *n = size;

    return p - bufptr - 1;
}

int main(int argc, char** args) {
    char *buf = NULL; /*malloc(10);*/
    int bufSize = 0; /*10;*/

    printf("%d\n", bufSize);
    int charsRead =  getline(&buf, &bufSize, stdin);

    printf("'%s'", buf);
    printf("%d\n", bufSize);
    return 0;
}

15 Minuten, und ich habe seit 10 Jahren kein C mehr geschrieben. Es unterbricht den getline-Vertrag geringfügig, indem es nur prüft, ob der lineptr NULL ist, anstatt NULL und n == 0. Sie können das beheben, wenn Sie möchten. (Der andere Fall hat für mich nicht viel Sinn gemacht, ich denke, Sie könnten in diesem Fall -1 zurückgeben.)

Ersetzen Sie das ‘\n’ durch eine Variable, um “getdelim” zu implementieren.

Schreiben die Leute noch Code?

  • Dies funktioniert gut für kurze Zeichenfolgen, kann jedoch nach der Neuzuweisung fehlschlagen. bufptr kann eine neue Adresse erhalten und p muss auf dem gleichen relativen Offset gehalten werden. In meinen Tests (mit MinGW) kann realloc mehrmals mit demselben Zeiger zurückkehren (falls an dieser Stelle genügend Speicher vorhanden ist) oder bei der ersten Neuzuweisung eine neue Adresse zurückgeben. Die neue Adresse kann in der Nähe des Speichers oder weit entfernt sein und kann sowohl vor als auch nach der ersten Adresse liegen. IE kann es eine Zufallszahl machen. Um das Problem zu beheben, geben Sie “offset = p – bufptr;” ein. unter der while EOF-Zeile und “p = bufptr + offset;” nach dem if NULL-Block.

    – Todd

    29. April 2010 um 4:52 Uhr

  • ((p - bufptr) > (size - 1)) ist ein Problem, wenn size == 0 (und *lineptr war ungewöhnlicherweise nicht NULL) als size - 1 ist ein groß Nummer. Empfehlen ((p - bufptr + 1) > size).

    – chux – Wiedereinsetzung von Monica

    9. Dezember 2014 um 15:51 Uhr


  • malloc und realloc geben meine stdio.h-Code-void*-Zeiger zurück. Also musste ich Cast-Operatoren hinzufügen, auch (char*) für die beiden Zeilen.

    – Marmelade

    14. Dezember 2015 um 9:32 Uhr

  • Sie können nicht -1 in zurückgeben size_t. Dies wird kläglich scheitern, wenn etwas schief geht.

    – Lilith-Fluss

    31. März 2016 um 21:31 Uhr

  • BEACHTEN SIE, DASS DIES getline UMSETZUNG IST SEHR DEFEKT wie von @Todd hervorgehoben. NICHT ÜBERALL VERWENDEN.

    – Antti Haapala – Слава Україні

    10. November 2017 um 18:22 Uhr

Antti Haapala -- Benutzeravatar von Слава Україні
Antti Haapala – Слава Україні

Der Code von Will Hartung leidet unter einem sehr ernsten Problem. realloc wird höchstwahrscheinlich den alten Block freigeben und einen neuen zuweisen, aber der p Der Zeiger innerhalb des Codes zeigt weiterhin auf das Original. Dieser versucht, das zu beheben, indem er stattdessen die Array-Indizierung verwendet. Es versucht auch, die Standard-POSIX-Logik genauer zu replizieren.

/* The original code is public domain -- Will Hartung 4/9/09 */
/* Modifications, public domain as well, by Antti Haapala, 11/10/17
   - Switched to getc on 5/23/19 */

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

// if typedef doesn't exist (msvc, blah)
typedef intptr_t ssize_t;

ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
    size_t pos;
    int c;

    if (lineptr == NULL || stream == NULL || n == NULL) {
        errno = EINVAL;
        return -1;
    }

    c = getc(stream);
    if (c == EOF) {
        return -1;
    }

    if (*lineptr == NULL) {
        *lineptr = malloc(128);
        if (*lineptr == NULL) {
            return -1;
        }
        *n = 128;
    }

    pos = 0;
    while(c != EOF) {
        if (pos + 1 >= *n) {
            size_t new_size = *n + (*n >> 2);
            if (new_size < 128) {
                new_size = 128;
            }
            char *new_ptr = realloc(*lineptr, new_size);
            if (new_ptr == NULL) {
                return -1;
            }
            *n = new_size;
            *lineptr = new_ptr;
        }

        ((unsigned char *)(*lineptr))[pos ++] = c;
        if (c == '\n') {
            break;
        }
        c = getc(stream);
    }

    (*lineptr)[pos] = '\0';
    return pos;
}

Die Leistung kann für eine Plattform erhöht werden, indem der Stream einmal gesperrt und das Äquivalent von verwendet wird getc_unlocked(3) – aber diese sind nicht in C standardisiert; und wenn Sie die POSIX-Version verwenden, werden Sie wahrscheinlich haben getline(3) schon.

  • Ich habe diese Fehler: error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive] für dein malloc(128) und realloc(*lineptr, new_size). Ich habe es behoben, indem ich sie gecastet habe (char*): ungültige Konvertierung von void*' to char*’ bei Verwendung von malloc?

    – Benutzer

    23. Mai 2019 um 15:01 Uhr

  • Als ich mit Cygwin C getestet habe, war die Leistung 10-mal schlechter als die eingebaute getline()

    – Benutzer

    23. Mai 2019 um 15:06 Uhr


  • Was die Leistung betrifft, so wird das erwartet, da ich es verwende fgetc die den Stream für jedes gelesene Zeichen sperren muss. Leider gibt es keine standardkonforme Möglichkeit, das Lock-Unlock zu umgehen. Es gibt für POSIX, aber wenn Sie POSIX haben, werden Sie wahrscheinlich auch getline haben.

    – Antti Haapala – Слава Україні

    23. Mai 2019 um 16:23 Uhr

  • @user an Fenster Sie können verwenden _lock_file und _getc_nolock

    – Antti Haapala – Слава Україні

    23. Mai 2019 um 16:29 Uhr


Verwenden Sie diese portablen Versionen von NetBSD: getdelim() und getline()

Diese kommen von libnbcompat in pkgsrc und haben eine BSD-Lizenz oben in jeder Datei. Sie brauchen beides, weil getline() getdelim() aufruft. Holen Sie sich die neuesten Versionen beider Dateien. Siehe die BSD-Lizenz oben in jeder Datei. Passen Sie die Dateien an Ihr Programm an: Möglicherweise müssen Sie getline() und getdelim() in einer Ihrer Header-Dateien deklarieren und beide Dateien so ändern, dass sie Ihren Header anstelle der nbcompat-Header enthalten.

Diese Version von getdelim() ist portabel, weil sie fgetc() aufruft. Im Gegensatz dazu würde ein getdelim() von einer libc (wie BSD libc oder musl libc) wahrscheinlich private Funktionen dieser libc verwenden, so dass es nicht plattformübergreifend funktionieren würde.

In den Jahren seitdem POSIX 2008 spezifiziert getline(), weitere Unixish-Plattformen haben die Funktion getline() hinzugefügt. Es ist selten, dass getline() fehlt, aber es kann immer noch auf alten Plattformen passieren. Ein paar Leute versuchen, NetBSD pkgsrc auf alten Plattformen (wie PowerPC Mac OS X) zu booten, also wollen sie, dass libnbcompat fehlende POSIX-Funktionen wie getline() bereitstellt.

Wenn Sie für BSD kompilieren fgetln
stattdessen

Benutzeravatar von naren vikram raj
naren vikram raj

Versuchen Sie, fgets() anstelle von getline() zu verwenden. Ich habe getline() unter Linux verwendet und es hat gut funktioniert, bis ich zu Windows migriert bin. Das Visual Studio hat getline() nicht erkannt. Also ersetze ich den Zeichenzeiger durch Zeichen und EOF durch NULL. Siehe unten:

#define CHARCOUNT 1000

Vor:

char *line = (char*) malloc(CHARCOUNT);
size_t size;
FILE *fp = fopen(file, "r");
while(getline(&line, &size, fp) != -1) {
   ...
}
free(line);

Nach:

char line[CHARCOUNT];
while(fgets(line, CHARCOUNT, fp) != NULL) {
   ...
}

  • Dadurch werden keine Zeilen behandelt, die länger als die maximal übergebene Länge sind fgets(), längere Zeilen werden aufgeteilt. Es ändert also die Semantik des Programms erheblich. Code verwenden getline() erwartet implizit, eine Zeile beliebiger Länge lesen zu können, also ersetzen getline() mit fgets() ist ein latenter Fehler.

    – Andreas Henle

    11. September 2021 um 10:56 Uhr

  • Dadurch werden keine Zeilen behandelt, die länger als die maximal übergebene Länge sind fgets(), längere Zeilen werden aufgeteilt. Es ändert also die Semantik des Programms erheblich. Code verwenden getline() erwartet implizit, eine Zeile beliebiger Länge lesen zu können, also ersetzen getline() mit fgets() ist ein latenter Fehler.

    – Andreas Henle

    11. September 2021 um 10:56 Uhr

1432880cookie-checkGibt es alternative Implementierungen der GNU-Getline-Schnittstelle?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy