2013-10-18 21 views
1

我从http://www.dwheeler.com/essays/filenames-in-shell.html写了一个dululppb实用程序,因为源代码的链接被破坏了,我想尝试学习D.我注意到它很慢(几乎不能跟上find -print0正在传递它的数据,因为它应该快得多,因为它不需要接近任何系统调用)。为什么这些小型D程序的行为有所不同?

第一个实现可以正常工作(使用zsh和bash printf内置函数以及/ usr/bin/printf进行测试)。第二种方法虽然要快得多(可能是因为调用write()的次数要少得多),但它会多次重复其输出的第一部分,并且无法输出其余部分输出。是什么造成这种差异?我是D的新手,不理解。

工作代码:

import std.stdio; 
import std.conv; 

void main() 
{ 
    foreach (ubyte[] mybuff; chunks(stdin, 4096)) { 
    encodebuff (mybuff); 
    } 
} 

@safe void encodebuff (ubyte[] mybuff) { 
    foreach (ubyte i; mybuff) { 
    char b = to!char(i); 
    switch (i) { 
     case 'a': .. case 'z': 
     case 'A': .. case 'Z': 
     case '0': .. case '9': 
     case '/': 
     case '.': 
     case '_': 
     case ':': writeChar(b); break; 
     default: writeOctal(b); break; 
     case 0: writeChar ('\n'); break; 
     case '\\': writeString(`\\`); break; 
     case '\t': writeString(`\t`); break; 
     case '\n': writeString(`\n`); break; 
     case '\r': writeString(`\r`); break; 
     case '\f': writeString(`\f`); break; 
     case '\v': writeString(`\v`); break; 
     case '\a': writeString(`\a`); break; 
     case '\b': writeString(`\b`); break; 
    } 
    } 
} 

@trusted void writeString (string a) 
{ 
    write (a); 
} 

@trusted void writeOctal (int a) 
{ 
    writef ("\\%.4o", a); // leading 0 needed for for zsh printf '%b' 
} 

@trusted void writeChar (char a) 
{ 
    write (a); 
} 

破碎的版本:

import std.stdio; 
import std.conv; 
import std.string; 

void main() 
{ 
    foreach (ubyte[] mybuff; chunks(stdin, 4096)) { 
    encodebuff (mybuff); 
    } 
} 

@safe void encodebuff (ubyte[] mybuff) { 
    char[] outstring; 
    foreach (ubyte i; mybuff) { 
    switch (i) { 
     case 'a': .. case 'z': 
     case 'A': .. case 'Z': 
     case '0': .. case '9': 
     case '/': 
     case '.': 
     case '_': 
     case ':': outstring ~= to!char(i); break; 
     case 0: outstring ~= '\n'; break; 
     default: char[5] mystring; 
     formatOctal(mystring, i); 
     outstring ~= mystring; 
     break; 
     case '\\': outstring ~= `\\`; break; 
     case '\t': outstring ~= `\t`; break; 
     case '\n': outstring ~= `\n`; break; 
     case '\r': outstring ~= `\r`; break; 
     case '\f': outstring ~= `\f`; break; 
     case '\v': outstring ~= `\v`; break; 
     case '\a': outstring ~= `\a`; break; 
     case '\b': outstring ~= `\b`; break; 
    } 
    writeString (outstring); 
    } 
} 

@trusted void writeString (char[] a) 
{ 
    write (a); 
} 

@trusted void formatOctal (char[] b, ubyte a) 
{ 
    sformat (b, "\\%.4o", a); // leading 0 needed for zsh printf '%b' 
} 

测试:(请注意,文件清单是找到-print0在我的家目录下生成文件的NUL分隔的列表, filelist2.txt由filelist生成,由filelist.txt sed -e's/\ x0/\ n/g'> filelist2.txt生成,因此是换行符分隔的文件名的相应列表)。

# the sed script escapes the backslashes so xargs does not clobber them 
diff filelist2.txt <(<filelist.txt char2code2 | sed -e 's/\\/\\\\/g' | xargs /usr/bin/printf "%b\n") 
# from within zsh 
bash -c 'diff filelist2.txt <(for i in "$(<filelist.txt char2code)"; do printf "%b\n" "$i"; done)' 
# from within zsh and bash 
diff filelist.txt <(for i in $(char2code <filelist.txt); do printf '%b\0' "$i"; done) 
# from within zsh, bash, and dash 
for i in $(char2code <filelist.txt); do printf '%b\0' "$i"; done | diff - filelist.txt 

我作为一个严峻的考验制作的脚本:

#!/bin/bash 
# this creates a completely random list of NUL-delimited strings 
a='' 
trap 'rm -f "$a"' EXIT 
a="$(mktemp)"; 
</dev/urandom sed -e 's/\x0\x0/\x0/g' | dd count=2048 of="$a" 
test -s "$a" || exit 1 
printf '\0' >> "$a" 
for i in $("[email protected]" < "$a") 
do 
    printf '%b\0' "$i" 
done | diff - "$a" 

什么是差异的原因?

编辑:我已经实施了@ yaz和@MichalMinich建议的更改,我仍然看到错误的结果。具体来说,找到-print0 |从我的主目录中的char2code2(位于我的$ PATH中的程序的名称)导致退出状态为1并且没有输出。但是,它从子目录中运行的项目数量要少得多。新修改的源代码如下:

import std.stdio; 
import std.conv; 
import std.format; 
import std.array; 

void main() 
{ 
    foreach (ubyte[] mybuff; chunks(stdin, 4096)) { 
    encodebuff (mybuff); 
    } 
    writeln(); 
} 

void encodebuff (ubyte[] mybuff) { 
    auto buffer = appender!string(); 
    foreach (ubyte i; mybuff) { 
    switch (i) { 
     case 'a': .. case 'z': 
     case 'A': .. case 'Z': 
     case '0': .. case '9': 
     case '/': 
     case '.': 
     case '_': 
     case ':': buffer.put(to!char(i)); break; 
     case 0: buffer.put('\n'); break; 
     default: formatOctal(buffer, i); break; 
     case '\\': buffer.put(`\\`); break; 
     case '\t': buffer.put(`\t`); break; 
     case '\n': buffer.put(`\n`); break; 
     case '\r': buffer.put(`\r`); break; 
     case '\f': buffer.put(`\f`); break; 
     case '\v': buffer.put(`\v`); break; 
     case '\a': buffer.put(`\a`); break; 
     case '\b': buffer.put(`\b`); break; 
    } 
    } 
    writeString (buffer.data); 
    // writef(stderr, "Wrote a line\n"); 
} 

@trusted void writeString (string a) 
{ 
    write (a); 
} 

@trusted void formatOctal(Writer)(Writer w, ubyte a) 
{ 
    formattedWrite(w, "\\%.4o", a); // leading 0 needed for zsh printf '%b' 
} 

回答

1

真正的问题发生在我的LDC安装。它使用共享库,它的druntime版本不支持它。

将LDC重新编译为使用的静态库解决了该问题。

2

其中一个原因可能是你一直在追加5个字符的char[5] mystring。函数sformatformatOctal返回可能少于5个字符(可能是缓冲区片)的格式化字符串,您应该使用该字符串追加到outstring。

性能建议:使用Appender而不是〜=可以在构建字符串时获得更好的性能。

+0

慕尼黑这并没有解决问题 - 见编辑 – Demi

3

您需要在encodebuffforeach以外采取writeString。目前,您在每个循环上写入outstring而不清除它。 @Michal Minich指出的问题也是有效的。

+0

这没有解决问题 - 请参阅编辑 – Demi

+1

我在本地运行修改后的程序,它似乎正常运行,因为我无法发现任何问题。你能展示一个可以重现问题的单元测试吗?或者在gdb下运行该程序,找出它以非零状态码退出的原因。要做到这一点,如果您使用的是dmd,请使用-g -debug编译您的程序。 – yaz

+0

添加try-catch块表明在某些时候会抛出一个异常,并且所有后续的输出操作都会失败 - 即使是那些用C的puts()完成的操作。这是否指向LDC stdio中的错误? – Demi

相关问题