printf,logcat和\ n

时间:2012-05-25 19:53:46

标签: android ios c printf logcat

我有C代码,使用类似

的东西
printf("hello ");
// do something
printf(" world!\n");

输出

  

你好世界!

我想在Android和iOS上重用该代码,但Log.d()和NSLog()有效地在我传递的每个字符串的末尾添加换行符,以便输出此代码:

NSLog(@"hello ");
// do something
NSLog(@"world!\n");

出现(或多或少):

  

你好

     

世界!

我愿意用一些宏替换printf以使Log.d和NSLog模拟printf对'\ n'的处理;有什么建议吗?

4 个答案:

答案 0 :(得分:1)

可能有效的一个解决方案是定义一个全局日志函数,该函数在找到换行符之前不会刷新其缓冲区。

这里是java for android的一个(非常)简单版本:

import java.lang.StringBuilder;

class CustomLogger {
  private static final StringBuilder buffer = new StringBuilder();

  public static void log(String message) {
    buffer.append(message);

    if(message.indexOf('\n') != -1) {
      Log.d('SomeTag', buffer);
      buffer.setLength(0);
    }
  }
}

...
CustomLogger.log("Hello, ");
// Stuff
CustomLogger.log("world!\n"); // Now the message gets logged

它完全未经测试,但你明白了。
此特定脚本存在一些性能问题。例如,检查最后一个字符是否为换行符可能更好。


我刚刚意识到你想要用C语言。虽然标准的lib不会受到伤害(为了获得字符串缓冲区之类的东西),但它不应该太难移动。

答案 1 :(得分:0)

对于后代,this就是我所做的:将记录的字符串存储在缓冲区中,并在缓冲区中有换行符时在换行符之前打印部分。

答案 2 :(得分:0)

是的,NDK logcat对它很愚蠢。有一些方法可以将stderr / stdout重定向到logcat,但是有一些缺点(需要“adb shell setprop”,它只适用于root设备,或者是dup()类似的技术,但是为此目的创建一个线程并不是一件好事关于嵌入式设备的想法恕我直言,虽然你可以在下面进一步了解这项技术)。

所以我为此目的做了自己的函数/宏。这是片段。在debug.c中,执行以下操作:

#include "debug.h"
#include <stdio.h>
#include <stdarg.h>

static const char LOG_TAG[] = "jni";

void android_log(android_LogPriority type, const char *fmt, ...)
{
    static char buf[1024];
    static char *bp = buf;

    va_list vl;
    va_start(vl, fmt);
    int available = sizeof(buf) - (bp - buf);
    int nout = vsnprintf(bp, available, fmt, vl);
    if (nout >= available) {
        __android_log_write(type, LOG_TAG, buf);
        __android_log_write(ANDROID_LOG_WARN, LOG_TAG, "previous log line has been truncated!");
        bp = buf;
    } else {
        char *lastCR = strrchr(bp, '\n');
        bp += nout;
        if (lastCR) {
            *lastCR = '\0';
            __android_log_write(type, LOG_TAG, buf);

            char *rest = lastCR+1;
            int len = bp - rest; // strlen(rest)
            memmove(buf, rest, len+1); // no strcpy (may overlap)
            bp = buf + len;
        }
    }

    va_end(vl);
}

然后在debug.h中执行以下操作:

#include <android/log.h>

void android_log(android_LogPriority type, const char *fmt, ...);
#define LOGI(...) android_log(ANDROID_LOG_INFO, __VA_ARGS__)
#define LOGW(...) android_log(ANDROID_LOG_WARN, __VA_ARGS__)
...

现在你只需要包含debug.hpp并使用类似printf的语义调用LOGI(),直到遇到'\ n'(或缓冲区已满)。

但这并不完美,好像从调用生成的字符串比缓冲区长,它将被截断并输出。但坦率地说,在大多数情况下,1024个字符应该足够了(甚至比这还少)。无论如何,如果发生这种情况,它会输出一个警告,以便您了解它。

另请注意,vsnprintf()不是标准C(但它适用于Android NDK)。我们可以使用vsprintf()代替(这是标准的),但它本身就不安全。

=============================================== =======================

现在对于dup()技术,你可以查看hereJames Moore回答)。

然后你可以摆脱上面的功能并将你的宏定义为:

#define LOG(...) fprintf(stderr, ...)

你已经完成了。

优点:

  1. C / C ++库通常使用stderr作为日志。使用dup是在logcat中输出而不修改代码的唯一方法(一些大的使用数百个直接调用fprintf(stderr,...))
  2. stderr是几十年来使用的标准C.与流相关的所有标准C库函数都可以与它一起使用。对于C ++也是如此,你甚至可以使用cerr和&lt;&lt;运营商。它在引擎盖下工作,它仍然是stderr。
  3. 非长线未被截断(相反,它们被分开)。使用较短缓冲区的一个很好的理由(在示例中为256)。
  4. 缺点:

    1. 一个线程(虽然它只是一个IO线程,影响几乎没有)
    2. 在通话过程中可以选择无日志优先级值(INFO,WARN,ERROR等...)。它使用默认值(INFO),因此DMMS将始终以相同的颜色显示stderr行。

答案 3 :(得分:-1)

你总是可以一次只构建一个字符串:

String message = "Hello";
// Do Something
message += " World!";
Log.v("Example", message);