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"。
获得了这个很棒的 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"。