繁体   English   中英

printf,logcat和\\ n

[英]printf, logcat and \n

我有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'的处理; 有什么建议么?

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

这是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不会受到伤害(为了获得像字符串缓冲区这样的东西),但它应该不会太难。

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

是的,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()技术,你可以看一下James Moore回答)。

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

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

你完成了

好处:

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

缺点:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM