2009-07-19 7 views
1

C# hat eine nette Funktion in der Lage zu schreiben in einen Speicher-Stream mit dem MemoryStream-Objekt.Können Sie in ein Byte-Array mit einem FILE schreiben *

Ich bin auf der Suche nach ähnlichen Funktionen von C mit einem FILE * -Zeiger.

Ich möchte in der Lage sein, sprintf(), aber mit der zusätzlichen Funktionalität von C zu erinnern, "wo ich war" in den Puffer, den ich schreibe.

+0

einen Code schreiben, der veranschaulicht, was Sie tun möchten –

+0

Denken Sie daran, dass Sie das tun können, ohne ein FILE * zu verwenden, der Rückgabewert von sprintf sagt Ihnen, wie viele Zeichen geschrieben wurden, also verwenden Sie das als Offset in den Puffer jedes Mal . – nos

Antwort

3

Es gibt auch eine hässliche Hack, die mit Leitungs ISO-C arbeitet: Sie fopen() verwenden können, eine Null-Datei zu öffnen (/dev/null auf * nix, NUL unter Windows), und stellen Sie das Array als Puffer der Datei über

setvbuf(file, buffer, _IOFBF, buffer_size) 

Dies sollte gut funktionieren, solange fflush() nicht irgendwo im Code aufgerufen wird. Außerdem muss sich der Programmierer explizit um den String-Begrenzer kümmern.

Ich sehe keine Notwendigkeit, dies zu tun: Wie snprintf() die Anzahl der geschriebenen Zeichen zurückgibt, ist es trivial, die Position eines Puffers zu verfolgen.

Man kann sogar eine Funktion schreiben, automatisch den Pufferüberlauf, um die Größe: bufprintf.c

der Prototyp der Funktion

int bufprintf(char **buffer, size_t *size, size_t *offset, 
    const char *format, ...); 

ist ein Beispielprogramm könnte wie folgt aussehen:

#include <stdio.h> 

extern int bufprintf(char **buffer, size_t *size, size_t *offset, 
    const char *format, ...); 

int main(void) 
{ 
    size_t size = 0; // must be set! 
    size_t offset; 
    char * buffer; 

    for(int i = 0; i < 100; ++i) 
     bufprintf(&buffer, &size, &offset, "we rock %i\n", i); 

    puts(buffer); 
    printf("size:\t%u\noffset:\t%u\n", (unsigned)size, (unsigned)offset); 
} 
+0

Dies ist ein süßer Hack. – bobobobo

+0

Dies ist die "richtige" Antwort auf die Frage, aber ich habe Angst, es so zu markieren, weil es ein "Hack" genannt wird. Kann jemand diese Praxis bestätigen? – bobobobo

+0

Von dem, was ich sagen kann, ist es vollkommen legal, aber ich würde keinen neuen Code schreiben, um das zu tun. Es ist eine einfache und nette Möglichkeit, vorhandenen Code, der fest codiert ist, in eine Datei zu schreiben. – arke

0

Warum nicht mmap verwenden? Sie können eine Datei im Speicher ablegen und einen FILE * -Zeiger verwenden.

Update: Es funktioniert nicht. Es tut uns leid.

+0

Nein, das geht nicht. mmap funktioniert nur in Seiteneinheiten und muss zurückgesetzt werden, wenn sich die Dateigröße ändert. Darüber hinaus führt das Ändern der Datei über normale E/A-Kanäle, während sie mmaped ist, zu undefiniertem Verhalten. – bdonlan

0

MemoryStream ist keine Funktion von C#, der Sprache. Es ist einfach eine Klasse in der BCL. Sie könnten es selbst in C# schreiben.

Und so ist es in C - einige Funktionen schreiben würde, die fopen ähnlich im Einsatz arbeiten, fprintf, fwrite, fclose usw., sondern geben ihnen Namen wie mopen, mwrite usw. (vermutlich) und schreiben Sie arbeiten also auf einem Speicherpuffer.

5

Wenn Sie die GNU C-Bibliothek verwenden, können Sie fmemopen() verwenden. Es kann andere nicht portable Erweiterungen für andere Umgebungen geben, aber es gibt keine übertragbare Möglichkeit, FILE * s zu verwenden.

Sie könnten jedoch auch snprintf umbrechen, wenn Sie nicht darauf bestehen, FILE * s tatsächlich zu verwenden. Zum Beispiel hat glib (Anmerkung: nicht dasselbe wie die GNU C-Bibliothek und portabel) eine g_string_append_printf, die das tut, was Sie wollen.

+0

glibc hat auch 'open_memstream', das Sie verwenden sollten, wenn Sie die Größe der Daten nicht kennen, die Sie im Voraus schreiben werden. Siehe http://linux.die.net/man/3/fmemopen –

0

Wie über die manuelle Verwaltung Ihrer Zeiger. Ich habe nicht einen Compiler vor mir, aber hoffentlich der Code unten auf die Idee kommt across:

char huge_buffer[REALLY_BIG_SIZE]; 
char *write_pos = huge_buffer; 

//... 

void fprintf_to_mem(char **mem_ptr, const char *fmt, ...) 
{ 
    va_list args; 
    int num_written; 

    va_start(args, mem_ptr); 
    num_written = vsprintf(*write_pos, fmt, args); 
    *write_pos += num_written; 

    va_end(args); 
} 

//... 

fprintf_to_mem(&write_pos, "Testing %d %d %d", 1, 2, 3); 
fprintf_to_mem(&write_pos, "Hello world!\r\n"); 

Ich würde erwarten, dass die Ausgabe „Testing 1 2 3Hello Welt \ r \ n“ huge_buffer.

+0

Eine gute Idee, aber Sie behalten die Puffergröße nicht im Auge, so dass Sie anfällig für Pufferüberläufe sind (und Sie sonst viel Speicher verschwenden); Daher bearbeiten Sie auch nicht dynamisch die Größe des Arrays, wenn es voll ist. Es ist auch nicht Multithread-sicher, da es globale Variablen verwendet. –

+0

Der obige Code illustriert einfach das Konzept, ich habe nicht die gesamte Fehlerprüfung usw. einbezogen. Die Variablen müssen nicht unbedingt global sein, ich habe nur ihre Typen angegeben und sie würden außerhalb der Funktion definiert werden. Sie könnten in jedem gewünschten Umfang existieren (Funktionsumfang/Dateiumfang usw.). Danke, dass du auf dieses Zeug hingewiesen hast, obwohl ich angenommen hatte, dass er all das in Betracht ziehen würde. – Gabe

+0

Ich nahm auch an, dass er einen Puffer hatte, den er füllte. Ich denke, das ist nicht wirklich eine faire Annahme. Wenn er etwas mehr wie den STL std :: vector wollte, dann würde das offensichtlich viel mehr Arbeit erfordern! – Gabe

2

sprintf gibt die Anzahl der Zeichen zurück, die in die Zeichenfolge gedruckt wurden. Sie können diesen Wert verwenden, um den Zeiger Ihres Puffers zu erhöhen.

buffer += sprintf(buffer, "%d", i); 

Stellen Sie sicher, dass Sie um eine Kopie des ursprünglichen Zeiger zu halten, wie das, was Sie verwenden werden, wenn an anderer Stelle die Puffer übergeben.

Verwandte Themen