我正在使用NSTask从/ usr / bin / man获取输出。我得到输出但没有格式化(粗体,下划线)。应该是这样的东西:
带有下划线 的粗体文字
(注意斜体文字实际上是带下划线的,这里没有格式化)
而是像这样返回:
带有_u_n_d_e_r_l_i_n_e
的BBoolldd文本我在http://cl.ly/052u2z2i2R280T3r1K3c有一个可以下载和运行的最小测试项目;注意窗户什么都不做;输出将记录到控制台。
我认为我需要以某种方式手动解释NSData对象,但我不知道从哪里开始。理想情况下,我希望将其转换为NSAttributedString,但第一项业务实际上是消除重复和下划线。有什么想法吗?
答案 0 :(得分:4)
你的实际目的是什么?如果要显示手册页,可以选择将其转换为HTML并使用Web视图进行渲染。
解析man
的输出可能很棘手,因为默认情况下它由groff
使用终端处理器处理。这意味着输出被定制为在终端设备上显示。
一种替代解决方案是确定手册页源文件的实际位置,例如
$ man -w bash
/usr/share/man/man1/bash.1.gz
并使用groff
(ASCII近似值)和-a
(禁用颜色输出)手动调用-c
,例如
$ gunzip -c /usr/share/man/man1/bash.1.gz | groff -c -a -Tascii -man
这将导致ASCII文件没有大部分格式。要生成HTML输出,
$ gunzip -c /usr/share/man/man1/bash.1.gz | groff -Thtml -man
您还可以在man
的自定义配置文件中指定这些选项,例如parseman.conf,并告诉man
将该配置文件与-C
选项一起使用,而不是调用man -w
,gunzip
和groff
。默认配置文件为/private/etc/man.conf
。
此外,您可以通过将适当的选项传递给grotty
来定制终端设备处理器的输出。
答案 1 :(得分:2)
好的,这是我的解决方案的开始,虽然我会对任何其他(更容易?)方法感兴趣。
终端返回的输出是UTF-8编码,但NSUTF8StringEncoding不能正确解释字符串。原因是NSTask输出的格式化方式。
字母N在UTF-8中是0x4e。但对应的NSData是0x4e 0x08 0x4e。 0x08对应退格。所以对于一个粗体字母,终端打印字母后退字母。
对于斜体c,UTF-8为0x63。 NSData包含0x5f 0x08 0x63,0x5f对应于下划线。所以对于斜体,终端打印下划线 - 退格字母。
除了扫描这些序列的原始NSData之外,我现在还没有看到任何解决方法。除非任何人有任何现有代码,否则我可能会在完成后将源发布到我的解析器。正如常见的编程短语一样,永远不要写自己可以复制的内容。 :)
后续:
我有一个好的,快速的解析器,用于获取man输出并用NSMutableAttributedString中的粗体/下划线格式替换粗体/带下划线的输出。如果其他人需要解决同样的问题,这是代码:
NSMutableIndexSet *boldChars = [[NSMutableIndexSet alloc] init];
NSMutableIndexSet *underlineChars = [[NSMutableIndexSet alloc] init];
char* bBytes = malloc(1);
bBytes[0] = (char)0x08;
NSData *bData = [NSData dataWithBytes:bBytes length:1];
free(bBytes); bBytes = nil;
NSRange testRange = NSMakeRange(1, [inputData length] - 1);
NSRange bRange = NSMakeRange(0, 0);
do {
bRange = [inputData rangeOfData:bData options:(NSDataSearchOptions)NULL range:testRange];
if (bRange.location == NSNotFound || bRange.location > [inputData length] - 2) break;
const char * buff = [inputData bytes];
if (buff[bRange.location - 1] == 0x5f) {
// it's an underline
//NSLog(@"Undr %c\n", buff[bRange.location + 1]);
[inputData replaceBytesInRange:NSMakeRange(bRange.location - 1, 2) withBytes:NULL length:0];
[underlineChars addIndex:bRange.location - 1];
testRange = NSMakeRange(bRange.location, [inputData length] - (bRange.location));
} else if (buff[bRange.location - 1] == buff[bRange.location + 1]) {
// It's a bold
//NSLog(@"Bold %c\n", buff[bRange.location + 1]);
[inputData replaceBytesInRange:NSMakeRange(bRange.location - 1, 2) withBytes:NULL length:0];
[boldChars addIndex:bRange.location - 1];
testRange = NSMakeRange(bRange.location, [inputData length] - (bRange.location));
} else {
testRange.location = bRange.location + 1;
testRange.length = [inputData length] - testRange.location;
}
} while (testRange.location <= [inputData length] - 3);
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:[[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding]];
NSFont *font = [NSFont fontWithDescriptor:[NSFontDescriptor fontDescriptorWithName:@"Menlo" size:12] size:12];
NSFont *boldFont = [[NSFontManager sharedFontManager] convertFont:font toHaveTrait:NSBoldFontMask];
[str addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, [str length])];
__block NSUInteger begin = [underlineChars firstIndex];
__block NSUInteger end = begin;
[underlineChars enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
if (idx - end < 2) {
// it's the next item to the previous one
end = idx;
} else {
// it's a split, so drop in the accumulated range and reset
[str addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:NSMakeRange(begin, (end-begin)+1)];
begin = idx;
end = begin;
}
if (idx == [underlineChars lastIndex]) {
[str addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSSingleUnderlineStyle] range:NSMakeRange(begin, (end-begin)+1)];
}
}];
begin = [boldChars firstIndex];
end = begin;
[boldChars enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
if (idx - end < 2) {
// it's the next item to the previous one
end = idx;
} else {
// it's a split, so drop in the accumulated range and reset
[str addAttribute:NSFontAttributeName value:boldFont range:NSMakeRange(begin, (end-begin)+1)];
begin = idx;
end = begin;
}
if (idx == [underlineChars lastIndex]) {
[str addAttribute:NSFontAttributeName value:boldFont range:NSMakeRange(begin, (end-begin)+1)];
}
}];
答案 2 :(得分:1)
另一种方法是将手册页转换为PostScript源代码,通过PostScript-to-PDF转换器运行,并将其放入PDFView。
实施类似于Bavarious的答案,只是对groff的不同论点(-Tps
而不是-Thtml
)。
这将是最慢的解决方案,但也可能是打印的最佳解决方案。