1

ソースコードへのリンクが壊れていて、D を学びたいと思ったので、http://www.dwheeler.com/essays/filenames-in-shell.htmlから nul2pfb ユーティリティの D 実装を書きました。それはかなり遅かったことです(データを渡す find -print0 にほとんど追いつくことができませんでしたが、システムコールの近くで行う必要がないため、はるかに高速である必要があります)。

最初の実装は正しく動作します (zsh と bash の printf 組み込み、および /usr/bin/printf でテスト済み)。2 番目は、はるかに高速ですが (おそらく 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'
}

テスト: (filelist はホーム ディレクトリで find -print0 によって生成された NUL 区切りのファイルのリストであり、filelist2.txt は filelist.txt によって filelist から生成されることに注意してください 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 $("$@" < "$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'
}
4

3 に答える 3

2

理由の 1 つは、常に 5 文字のchar[5] mystring. 関数sformatinformatOctalは、5 文字未満 (おそらくバッファーのスライス) のフォーマットされた文字列を返します。その文字列を使用して outstring に追加する必要があります。

パフォーマンスに関するアドバイス: ~= の代わりにAppenderを使用すると、文字列を構築する際のパフォーマンスが向上します。

于 2013-10-18T19:18:17.567 に答える
1

本当の問題は、LDC のインストールにあることが判明しました。そのバージョンの druntime ではサポートされていない共有ライブラリを使用していました。

LDC を使用済みの静的ライブラリに再コンパイルすると、問題が修正されました。

于 2013-11-15T23:17:17.313 に答える