C hack:使用行缓冲区替换 printf 以收集输出和 return 完整字符串

C hack: replace printf to collect output and return complete string by using a line buffer

获得了这个很棒的 C 程序,我想将其嵌入到 iOs 应用程序中。 将命令行参数传递给它,结果通过 printf 和 fputs 打印到标准输出——就像所有优秀的旧 unix 程序一样。

现在我只想编辑 main 和 print 函数以使用我自己的 printf 函数,该函数收集通常转到 stdout 的所有输出并在最后 return 它。

我通过使用行缓冲区收集所有 printfs 直到换行符来实现解决方案。 以及当输出行完成时我复制到的动态字符数组。

此解决方案的魅力在于 - 它有点像 tcl'ish:只需将所有内容放入一个文本行中,如果它完整存储它。现在,只要有必要就这样做,最后 return 全部...

这里是问题: 它有效 - 但由于我在 "real" 编程方面相当陌生 - 即 C 和 Apples "brandnew" Swift - 我不确定这是否是一个好的解决方案。是吗?如果没有 - 你会建议什么?非常感谢!

C代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

// outLineBuffer collects one output line by several calls to tprntf
#define initialSizeOfReturnBuffer 10 // reduced for testing (would be 16*1024)
#define incrSizeOfReturnBuffer 5     // reduced for testing (would be 1024*1024)
#define outLineBufferMaxSize 4095
char outLineBuffer[sizeof(char)*outLineBufferMaxSize] = "";
char *tReturnString;
size_t sizeOfReturnBuffer, curPosOutBuffer = 0, lenOutLine = 0;

所有原始 printf 和 fputs 的替换 tprntf:

// replace printf with this to collect the parts of one output line.
static int tprntf(const char *format, ...)
{
    const size_t maxLen = sizeof(char)*outLineBufferMaxSize;
    va_list arg;
    int done;

    va_start (arg, format);
    done = vsnprintf (&outLineBuffer[lenOutLine], maxLen-lenOutLine, format, arg);
    va_end (arg);
    lenOutLine = strlen(outLineBuffer);

    return done;
}

以及当我们完成一个输出行时的函数(到处打印\n):

// Output line is now complete: copy to return buffer and reset line buffer.
static void tprntNewLine()
{
    size_t newSize;
    long remainingLenOutBuffer;
    char *newOutBuffer;

    remainingLenOutBuffer = sizeOfReturnBuffer-curPosOutBuffer-1;
    lenOutLine = strlen(outLineBuffer)+1; // + newline character (\n)
    remainingLenOutBuffer -= lenOutLine;

    if (remainingLenOutBuffer < 0) {
        newSize = sizeOfReturnBuffer + sizeof(char)*incrSizeOfReturnBuffer;
        if ((newOutBuffer = realloc(tReturnString, newSize)) != 0) {
            tReturnString = newOutBuffer;
            sizeOfReturnBuffer = newSize;
        } else {
            lenOutLine += remainingLenOutBuffer; //just write part that is still available
            remainingLenOutBuffer = 0;
        }
    }

    snprintf(&tReturnString[curPosOutBuffer], lenOutLine+1, "%s\n", outLineBuffer);

    curPosOutBuffer += lenOutLine;
    outLineBuffer[0] = 0;
    lenOutLine = 0;
}

还有一点主要测试它(没有 swift - 例如普通 gcc):

int main(int argc, char *argv[])
{
    int i;
    sizeOfReturnBuffer = initialSizeOfReturnBuffer*sizeof(char);
    if ((tReturnString = malloc(sizeOfReturnBuffer)) == 0) {
        return 1; // "Sorry we are out of memory. Please close other apps and try again!";
    }
    tReturnString[0] = 0;

    for (i = 1; i < argc; i++) {
        tprntf("%s ", argv[i]);
    }
    tprntNewLine();

    tprntf("%s", "ABC\t");
    tprntf("%d", 12);
    tprntNewLine(); // enough space for that ;-)
    tprntf("%s", "DEF\t");
    tprntf("%d", 34);
    tprntNewLine(); // realloc necessary ...
    tprntf("%s", "GHI\t");
    tprntf("%d", 56);
    tprntNewLine(); // again realloc for testing purposes ...
    printf("tReturnString at the end:\n>%s<\n", tReturnString); // contains trailing newline
    return 0;
}

来自 swift 的调用如下(使用 CStringArray.swift

let myArgs = CStringArray(["computeIt", "par1", "par2"])
let returnString = mymain(myArgs.numberOfElements, &myArgs.pointers[0])

if let itReturns = String.fromCString(returnString) {
    print(itReturns)
}

freeMemory()

我相信tcl有很多优化,我建议你也优化代码;那么你的方法是可行的。

检查您对 strlen 的频繁使用,它每次都会遍历所有(许多)字符来计算长度 - 使用有关其长度的信息,例如维护 char *outLineBufPtr。还可以使用 strcat 将 \n 附加到 outLineBuffer 而不是使用昂贵的 vsnprintf 函数,或者只是手动复制字符,如 *outLineBufPtr++ ='\n'; .

要实现像你这样的更高层次的概念,你必须开始用机器周期来思考,这样更高层次的概念就不会变成"expensive"。