How to redirect STDOUT to a NSTextView?

后端 未结 3 814
旧巷少年郎
旧巷少年郎 2021-02-15 14:13

Could anybody show me how to redirect the Stdout to a NSTextView?

and whether the info print by NSLog belong to the std?

Thanks

相关标签:
3条回答
  • 2021-02-15 14:18

    I know the question was about objective-c but I thought I would post a swift answer incase that helps someone else.

    let pipefd = UnsafeMutablePointer<Int32>.allocate(capacity: 8)
    pipe(pipefd)
    
    dup2(pipefd[1], fileno(stdout))
    
    // Print something here
    
    let buf = UnsafeMutableRawPointer.allocate(byteCount: 1024, alignment: 0)
    read(pipefd[0], buf, 100)
    
    close(pipefd[1])
    
    let output = self.ptrToString(pointer: buf)
    if output != "" {
        // Do something with output
    }
    
    buf.deallocate()
    pipefd.deallocate()
    

    And here is the function I use to convert the pointer to a string:

    func ptrToString (pointer buf: UnsafeMutableRawPointer) -> String {
        let filteredArray = Array(UnsafeBufferPointer(start: buf.assumingMemoryBound(to: UInt8.self), count: 1024)).filter { item in
            return item != 0
        }
    
        return filteredArray
            .map { String(UnicodeScalar(UInt8($0))) }
            .joined()
            .components(separatedBy: "\n")[0]
    }
    

    Works on swift 4

    0 讨论(0)
  • 2021-02-15 14:20

    The code below uses dup2 to plug stdout onto the write-end of an NSPipe object. The read-end is observed with a GCD dispatch source, that reads data from the pipe and appends it to a textview.

    NSPipe* pipe = [NSPipe pipe];
    NSFileHandle* pipeReadHandle = [pipe fileHandleForReading];
    dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stdout));
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, [pipeReadHandle fileDescriptor], 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
    dispatch_source_set_event_handler(source, ^{
        void* data = malloc(4096);
        ssize_t readResult = 0;
        do
        {
            errno = 0;
            readResult = read([pipeReadHandle fileDescriptor], data, 4096);
        } while (readResult == -1 && errno == EINTR);
        if (readResult > 0)
        {
            //AppKit UI should only be updated from the main thread
            dispatch_async(dispatch_get_main_queue(),^{
                NSString* stdOutString = [[NSString alloc] initWithBytesNoCopy:data length:readResult encoding:NSUTF8StringEncoding freeWhenDone:YES];
                NSAttributedString* stdOutAttributedString = [[NSAttributedString alloc] initWithString:stdOutString];
                [self.logView.textStorage appendAttributedString:stdOutAttributedString];
            });
        }
        else{free(data);}
    });
    dispatch_resume(source);
    

    NSLog(@"...") does not output to stdout though - It prints to stderr. If you want to redirect that into your textview, change

    dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stdout));
    

    to

    dup2([[pipe fileHandleForWriting] fileDescriptor], fileno(stderr));
    
    0 讨论(0)
  • 2021-02-15 14:40

    if the goal is to handle your NSLog output only, not the system generated error logs, there is an other way to do so, here is a code to overclass NSLog. This code is only printing the log and some extra information on stderr instead of the usual NSLog output, but you can make any changes that suites your needs inside the HyperLog function :

    HyperLog.h

    #import <Foundation/Foundation.h>
    
    #ifdef HYPER_LOG
    #define NSLog(args...) HyperLog(__FILE__,__LINE__,__PRETTY_FUNCTION__,args);
    #else
    #define NSLog(x...)
    #endif
    
    void HyperLog(const char *file, int lineNumber, const char *functionName, NSString *format, ...);
    

    Hyperlog.m

    #import "HyperLog.h"
    
    void HyperLog(const char *file, int lineNumber, const char *functionName, NSString *format, ...)
    {
        va_list ap;
        va_start (ap, format);
        if (![format hasSuffix: @"\n"])
        {
            format = [format stringByAppendingString: @"\n"];
        }
    
        NSString *body = [[NSString alloc] initWithFormat:format arguments:ap];
        va_end (ap);
         NSString *fileName = [[NSString stringWithUTF8String:file] lastPathComponent];
        char mesg[8192]="\0";
        NSDate *now =[NSDate date];
        NSString *dateString = [NSDateFormatter localizedStringFromDate:now dateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterMediumStyle];
        if ( sprintf( mesg, "<%s.%03.0f> : %s\n<%s : %d - %s>\n", [dateString UTF8String],roundf(fmod( [now timeIntervalSinceReferenceDate], 1 ) * 1000), [body UTF8String], [fileName UTF8String],
                     lineNumber,
                functionName) < 0 ) printf("message creation failed\n");
        fprintf(stderr, "%s", mesg );
    }
    

    You then only need to put these 2 lines on top of any of your program file to have it work

    #define HYPER_LOG
    #import "HyperLog.h"
    

    I tried to use the above code from Thomas to have the result data of a system generated error log written to a text file using a C function, that works properly in other contexts, but it keeps and crashing, and the error reason is lost in the process. Anyone has an idea why ?

    0 讨论(0)
提交回复
热议问题