Wie berechnet man die Länge der Ausgabe, die sprintf generiert?

Lesezeit: 6 Minuten

Benutzer-Avatar
Weißt du

Tor: Daten in JSON serialisieren.

Ausgabe: Ich kann vorher nicht wissen, wie viele Zeichen lang die ganze Zahl ist.

Ich dachte, ein guter Weg, dies zu tun, ist die Verwendung von sprintf()

size_t length = sprintf(no_buff, "{data:%d}",12312);
char *buff = malloc(length);
snprintf(buff, length, "{data:%d}",12312);
//buff is passed on ...

Natürlich kann ich eine Stapelvariable wie verwenden char a[256] Anstatt von no_buff.

Frage: Aber gibt es in C ein Dienstprogramm für Wegwerf-Schreibvorgänge wie Unix /dev/null? Etwas so:

#define FORGET_ABOUT_THIS ...
size_t length = sprintf(FORGET_ABOUT_THIS, "{data:%d}",12312);

ps Ich weiß, dass ich die Länge der Ganzzahl auch über das Protokoll erhalten kann, aber auf diese Weise scheint es schöner zu sein.

  • um die Länge zu ermitteln stackoverflow.com/questions/1068849/…

    – Keith Nicholas

    16. März 2015 um 21:26 Uhr


  • Mögliches Duplikat von Bestimmen der sprintf-Puffergröße – was ist der Standard?

    – Ciro Santilli OurBigBook.com

    19. März 2017 um 7:05 Uhr

Benutzer-Avatar
meinaut

Da C eine einfache Sprache ist, gibt es keine “wegwerfbaren Puffer” – die gesamte Speicherverwaltung liegt auf den Schultern des Programmierers (es gibt GNU C-Compiler-Erweiterungen dafür, aber sie sind kein Standard).

kann vorher nicht wissen, wie viele Zeichen lang die Ganzzahl ist.

Es gibt viel einfachere Lösung für Ihr Problem. snprintf weiß!

Auf C99-kompatiblen Plattformen rufen Sie snprintf mit NULL als erstem Argument auf:

ssize_t bufsz = snprintf(NULL, 0, "{data:%d}",12312);
char* buf = malloc(bufsz + 1);
snprintf(buf, bufsz + 1, "{data:%d}",12312);

...

free(buf);

Verwenden Sie in älteren Visual Studio-Versionen (mit nicht C99-kompatibler CRT). _scprintf Anstatt von snprintf(NULL, ...) Anruf.

  • Unterstützt Visual Studio (zumindest in neueren Versionen) eine Funktion namens nicht _snprintf das ist ähnlich dem Standard snprintf?

    – Keith Thompson

    16. März 2015 um 21:30 Uhr

  • @KeithThompson: MSDN überprüft, es scheint, dass sie das ab VS2013 behoben haben. Danke für den Hinweis, ich habe meine Antwort aktualisiert

    – myaut

    16. März 2015 um 21:33 Uhr


  • Notiz: snprintf() kann auch -1 zurückgeben und hat in Kurvensituationen Probleme als der Maximalwert von size_t und int sind selten gleich. Möglicherweise gibt es ein Problem mit der 2 snprintf() Sollte die locale Wechsel zwischen ihnen und der 2. `Ausgang ist länger.

    – chux – Wiedereinsetzung von Monica

    16. März 2015 um 22:03 Uhr

  • @chux: Natürlich ist mein Code generisch und sollte Fehlerprüfungen enthalten, einschließlich INT_MAX und -1und ssize_t ist kein Anliegen von snprintf(). Apropos Gebietsschema ändern – wir erhalten eine nicht-NULL-terminierte Zeichenfolge (also sollten wir die 2 snprintf() auch), aber zumindest wird es keinen Pufferüberlauf verursachen (zumindest direkt). AFAIK, es ist schwierig, das Gebietsschema extern zu ändern.

    – myaut

    16. März 2015 um 22:17 Uhr

Benutzer-Avatar
Brian Campell

Du kannst anrufen int len = snprintf(NULL, 0, "{data:%d}", 12312) um zu testen, wie viel Platz Sie benötigen.

snprintf wird höchstens gedruckt size Zeichen, wo size ist das zweite Argument und gibt zurück, wie viele Zeichen nötig gewesen wären, um das Ganze zu drucken, ohne das Ende zu zählen '\0'. Da Sie 0 übergeben, wird tatsächlich nichts ausgeschrieben (und somit jede Nullzeiger-Ausnahme vermieden, die beim Versuch einer Dereferenzierung auftreten würde NULL), aber es wird immer noch die Länge zurückgegeben, die für die gesamte Ausgabe erforderlich ist, die Sie zum Zuweisen Ihres Puffers verwenden können.

An diesem Punkt können Sie Ihren Puffer zuweisen und drucken, wobei Sie daran denken, einen weiteren für den Nachlauf einzufügen '\0':

char *buf = malloc(len + 1);
snprintf(buf, len + 1, "{data:%d}", 12312);

  • Vielen Dank. Genau das, was ich brauchte. Komisch, dass das nicht in der Kurzanleitung stand. Ich denke, darauf kommt es bei der Erfahrung an. Grüße

    – weißt du

    16. März 2015 um 21:33 Uhr

  • Sie sollten auch erwähnen, dass bei Verwendung eines NULL-Puffers die String-Beendigung nicht gezählt wird. de.cppreference.com/w/c/io/fprintf

    – weißt du

    16. März 2015 um 21:48 Uhr

  • @Aldi Danke für den Hinweis, aktualisiert, um diese Klarstellung aufzunehmen.

    – Brian Campell

    16. März 2015 um 21:55 Uhr

Um nur die Länge zu erhalten, können Sie schreiben:

int length = snprintf(NULL, 0, "{data:%d}", 12312);

Beachten Sie, dass der Rückgabetyp ist int. Es kann zurückkehren -1 im Falle eines Fehlers. Stellen Sie sicher, dass Ihre Eingabedaten keine langen Zeichenfolgen enthalten, die dazu führen könnten, dass die Gesamtlänge überschritten wird INT_MAX !

  • Sie können die maximale Größe (10 unter der Annahme von 32 Bit) + 8 – also 18 leicht berechnen

    – Ed heilen

    16. März 2015 um 21:29 Uhr

  • @EdHeal meine Lösung macht keine Annahmen über die Größen von Typen. Ich bin mir sicher, dass es eine Menge Code gibt, der genau aus diesem Grund kaputt gegangen ist, als er für ein 64-Bit-Ziel neu kompiliert wurde.

    – MM

    16. März 2015 um 21:31 Uhr

  • @mattMcNadd – Für die Geschwindigkeit besteht die Lösung nur darin, eine Konstante zu haben – dies ist ein Fall, in dem der Pro-Prozessor praktisch ist.

    – Ed heilen

    16. März 2015 um 21:34 Uhr

  • Meine Antworten konzentrieren sich auf Korrektheit über Geschwindigkeit 🙂 Mikrooptimierung kann später kommen. YMMV, nehme ich an.

    – MM

    16. März 2015 um 21:34 Uhr

  • @chux – Brunel ist einer meiner Helden. Damals haben sie das Zeug überarbeitet. Aber die Brücken usw. sind noch in Gebrauch. Aber die Forth-Straßenbrücke wird ersetzt, aber die Forth-Eisenbahnbrücke bekommt gerade einen neuen Anstrich, und bei der Clifton-Brücke könnten die Linien in der Mitte der Straße dieses Jahr neu gestrichen werden

    – Ed heilen

    16. März 2015 um 22:13 Uhr

Wenn Sie die Leistung überprüfen, wird die Ausführung von snprintf ohne Ausgabepuffer ungefähr so ​​lange dauern wie ein vollständiger Aufruf.

Daher empfehle ich Ihnen, für alle Fälle einen kleineren Puffer zu verwenden und ihn nur ein zweites Mal aufzurufen, wenn die zurückgegebene Größe die Puffergröße überschreitet.

Dies verwendet C++ std::string aber ich denke, du kannst es an deine Bedürfnisse anpassen.

std::string format(const char* format, ...) {
    va_list args;
    va_start(args, format);
    char smallBuffer[1024];
    int size = vsnprintf(smallBuffer, sizeof smallBuffer, format, args);
    va_end(args);

    if (size < sizeof smallBuffer) 
        return std::string(smallBuffer);

    char buffer[size  + 1]; /* maybe malloc if it's too big */

    va_start(args, format);
    vsnprintf(buffer, sizeof buffer, format, args);
    va_end(args);
    return std::string(buffer);
}

Dieser Code läuft 2x schneller für Strings unter 1k im Vergleich zu den längeren.

Der Aufruf von snprintf(nullptr, 0, …) gibt zwar die Größe zurück, hat aber Leistungseinbußen, da IO_str_overflow aufgerufen wird und das langsam ist.

Wenn Ihnen die Leistung wichtig ist, können Sie einen Dummy-Puffer vorab zuweisen und seinen Zeiger und seine Größe an ::snprintf übergeben. es wird um ein Vielfaches schneller sein als die nullptr-Version.

template<typename ...Args>
size_t get_len(const char* format, Args ...args) {
  static char dummy[4096]; // you can change the default size
  return ::snprintf(dummy, 4096, format, args...) + 1; // +1 for \0
}

  • Die Frage ist mit c gekennzeichnet; Warum haben Sie mit C++-Code geantwortet?

    – ad absurdum

    10. Januar 2020 um 5:25 Uhr

  • @exnihilo ah, sorry, ich habe das Tag verpasst

    – DAG

    10. Januar 2020 um 8:57 Uhr

Benutzer-Avatar
neunzigfan

Printf unterstützt den %n-Formatparameter, was bedeutet „Position von %n in der Ausgabezeichenfolge in den int-Wert schreiben, auf den der x-Parameter zeigt), also:

int x;snprintf(NULL, 0, "Message: %s%n", "Error!",&x);

Sollte funktionieren!

  • Die Frage ist mit c gekennzeichnet; Warum haben Sie mit C++-Code geantwortet?

    – ad absurdum

    10. Januar 2020 um 5:25 Uhr

  • @exnihilo ah, sorry, ich habe das Tag verpasst

    – DAG

    10. Januar 2020 um 8:57 Uhr

Benutzer-Avatar
Dolda2000

Dies ist nicht unbedingt eine Antwort auf Ihre Frage, aber Sie können es dennoch hilfreich finden. Es ist nicht portabel, aber wenn Sie es auf glibc ausführen, können Sie es einfach verwenden asprintf() stattdessen, was die Speicherzuweisung für Sie übernimmt.

1369440cookie-checkWie berechnet man die Länge der Ausgabe, die sprintf generiert?

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

Privacy policy