3

次のような賢いものでプリントを使用する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

4 に答える 4

1

有効な解決策の 1 つは、改行が見つかるまでバッファーをフラッシュしないグローバル ログ関数を定義することです。

Android用のJavaの(非常に)単純なバージョンは次のとおりです。

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 を使用しても問題はありません (文字列バッファーのようなものを取得するため)。

于 2012-05-25T21:30:07.807 に答える
0

はい、NDK logcat はそれについて馬鹿げています。stderr/stdout を logcat にリダイレクトする方法はありますが、欠点があります (ルート化されたデバイス専用の「adb shell setprop」が必要か、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 をインクルードし、'\n' が検出される (またはバッファーがいっぱいになる) までバッファーされた printf のようなセマンティックで LOGI() を呼び出すだけです。

ただし、呼び出しから生成された文字列がバッファよりも長い場合、切り捨てられて出力されるため、これは完全ではありません。しかし、率直に言って、ほとんどの場合、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 行を同じ色で表示します。
于 2012-08-05T01:31:41.397 に答える
0

子孫の場合、これが私がしたことです。ログに記録された文字列をバッファーに保存し、バッファーに改行があるたびに改行の前の部分を出力します。

于 2012-05-28T13:53:59.090 に答える
-1

一度に 1 セグメントずつ文字列を作成することもできます。

String message = "Hello";
// Do Something
message += " World!";
Log.v("Example", message);
于 2012-05-25T20:04:04.057 に答える