2013-10-18 36 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

相關問題