【问题标题】:printf, logcat and \nprintf、logcat 和 \n
【发布时间】:2012-05-25 19:53:45
【问题描述】:

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

【问题讨论】:

  • 我知道。我的问题是,如何在不破坏功能的情况下将 C 代码 printf 重定向到移动日志?我意识到需要一些额外的代码。
  • 引用自 文档:“* 请注意,换行符 ("\n") 将自动附加到您的 * 日志消息中,如果还没有的话。它不是可以发送多条 * 消息并让它们出现在 logcat 的一行中。”

标签: android ios c printf logcat


【解决方案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 中实现这一点。尽管标准库不会受到伤害(获取字符串缓冲区之类的东西),但移植应该不会太难。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      是的,NDK logcat 对此很愚蠢。有一些方法可以将 stderr/stdout 重定向到 logcat,但也有缺点(要么需要“adb shell setprop”,它仅适用于有根设备,要么需要类似 dup() 的技术,但仅为此目的创建线程并不好关于嵌入式设备的想法恕我直言,尽管您可以在下面进一步了解这种技术)。

      所以我为此目的做了自己的函数/宏。这里是sn-ps。在 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 并调用 LOGI() 并缓冲类似 printf 的语义,直到遇到“\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 与
      3. 很长的行没有被截断(相反,它们被分割了)。使用较短缓冲区(示例中为 256)的充分理由。

      缺点:

      1. 一个单独的线程(虽然它是一个仅 IO 线程,但影响几乎没有)
      2. 在通话期间不能选择日志优先级值(INFO、WARN、ERROR 等...)。它使用默认值 (INFO),因此 DMMS 将始终以相同颜色显示标准错误线。

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

        • 是的,但我希望在 C 中做到这一点。
        • 谢谢,我实际上已经在做类似的事情了。这是我所追求的 printf 换行行为。我有旧的 C 代码,它利用 printf 及其换行特性,我想在 iOS 和 Android 中使用。
        猜你喜欢
        • 2014-04-26
        • 2018-08-12
        • 2019-02-27
        • 1970-01-01
        • 1970-01-01
        • 2015-02-15
        • 1970-01-01
        • 1970-01-01
        • 2012-01-19
        相关资源
        最近更新 更多