web-dev-qa-db-ja.com

追加の書き込みなしにファイルに挿入できないのはなぜですか? (私は追加も上書きも意味しません)

これは、プログラミング言語に依存しない問題として発生します。

内容のファイルがあります

aaabddd

Cbの後ろに挿入したい場合、my codedddを書き換えて取得する必要があります

aaabCddd

この位置にCを挿入できないのはなぜですか?

Java、Pythonなどではできません。 Linux、Windowsなどではできません。私は正しいですか?

追加の書き込みなしではCを単純に挿入できない理由がわかりません。これがなぜそうであるのか誰かが説明してくれませんか?

8
User

ほとんどのファイルシステムは、物理ディスク上で必ずしも隣接していないが、ポインター構造を介してリンクされている個々のブロックにファイルの内容を格納するため、このようなモード(「追加」または「上書き」ではなく「挿入」)が必要です。コンテンツ全体を読み取り、バイトストリームを編集し、コンテンツ全体を再書き込みします。

ただし、良くも悪くも、ファイルシステムのUNIXセマンティクスは1970年代の「ラフでシンプルな」パラダイムに沿って設計されました。これにより、すべてを実行できますが、必ずしも最も効率的な方法ではありません。現在、仮想ファイルシステムレイヤーに新しいファイルオープンモードを導入し、主要なファイルシステムがそれをサポートすることを期待することはほとんど考えられません。これは私のうんざりですが、残念ながらすぐには解決されないでしょう。

8
Kilian Foth

理論的には、このようなことを可能にするファイルを実装できます。ただし、最大限の柔軟性を得るには、ファイル内のすべてのバイトと共に次のバイトへのポインタを格納する必要があります。 64ビットポインターを想定すると、ファイルの9バイトごとに8つが内部ポインターで構成されることになります。したがって、1000バイトの実際のデータを格納するには、9000バイトのスペースが必要になります。また、ディスクから大きな連続したデータブロックを読み取るのではなく、各バイトを読み取り、ポインターを読み取り、ポインターに従って次のバイトを読み取る必要があるため、ファイルの読み取りも遅くなります。

明らかに、この種のアプローチは実用的ではありません。ただし、ファイルをたとえば32 kbのブロックに分割することもできます。これにより、ファイル内の32 kbの境界に32 kbのデータを追加することが比較的簡単になります。ファイルの5番目のバイトとして1バイトを追加するのは簡単ではありません。ただし、すべてのブロックに空き領域を予約すると、その単一ブロックのデータにのみ影響を与えるデータの小さな追加が発生する可能性があります。もちろん、ファイルサイズの点でペナルティはありますが、合理的なペナルティになる可能性があります。ただし、予約する領域とブロックを分割する方法を理解することは、特定のアプリケーションの方が汎用システムよりもはるかに簡単な傾向があります。あるコンテキストで機能するものは、ファイルアクセスや修正特性。

実際、ファイルの操作に多くの時間を費やしている多くのシステムは、特定のファイル抽象化を実装するときに、上で説明したようなものを実装しています。たとえば、データベースは一般に、「ブロック」の概念をI/Oの最小単位として実装し、一般に将来の拡張のためにある程度のスペースを確保して、テーブルの行を更新することで、ファイル全体を書き換えるのではなく、そのデータが格納される1つのブロック。もちろん、データベースが異なれば実装も異なり、トレードオフも異なります。

12
Justin Cave

「問題」は、ファイルがバイト単位でストレージメディアに書き出される方法に要約されます。

最も基本的な表現では、ファイルはディスク(別名ストレージメディア)に書き込まれたseriesバイトにすぎません。したがって、元の文字列は次のようになります。

Address  Value
0x00     `a`
0x01     `a`
0x02     `a`
0x03     `b`
0x04     `d`
0x05     `d`
0x06     `d`

そして、位置0x04にCを挿入したいとします。これには、新しい値を挿入できるように、バイト4〜6を1バイト下にシフトする必要があります。そうしないと、現在0x04にある値が上書きされてしまい、必要な値が上書きされます。

Address  Value
0x00     `a`
0x01     `a`
0x02     `a`
0x03     `b`
0x04     `C`
0x05     `d`
0x06     `d`
0x07     `d`

したがって、新しい値を挿入した後にファイルの末尾を再書き込みする必要があるのは、挿入された値を受け入れるスペースがファイル内にないためです。そうしないと、そこにあったものを上書きします。


補遺1:必要に応じてreplaceb with C次に、文字列の末尾を書き換える必要はありません。値を同じサイズの値に置き換える場合、書き換えは必要ありません。

補遺2:文字列abCに置き換えたい場合は、wouldファイルにギャップを作成したので、残りのファイルを書き直す必要があります。

補遺3:大きなファイルを扱いやすくするために、ブロックレベルの構成が作成されました。 1M分のファイルの連続スペースを見つける必要はなく、代わりに1M分のブロックを見つけて書き込むだけで済みます。

理論的には、ブロックが提供するのと同様のバイト単位のリンクを行うファイルシステムを構築できます。次に、|を更新して新しいバイトを挿入できます。適切なポイントのポインタから。私はそのパフォーマンスがかなり悪いだろうという推測を危険にさらすでしょう。


グランドマスターBの提案 のように、積み重ねられたドミノの画像を使用して、ファイルがどのように表されているかを視覚的に理解します。

dominoes

すべてを転倒させることなく、ドミノの行内に別のドミノを挿入することはできません。他の人を線の下に移動して、新しいドミノ用のスペースを作成する必要があります。行の下にドミノを移動することは、挿入ポイントの後にファイルの末尾を書き換えることと同じです。

8
user53019

ファイルの途中にバイトのブロックを挿入する最も効率的な方法は、次のとおりです。

  1. ファイルをメモリにマッピングする
  2. ファイルのメモリイメージの最後にバイトを追加します。
  3. これらのファイルを所定の場所にローテーションします(たとえば、C++標準ライブラリで利用可能な標準アルゴリズムを使用)
  4. ダーティブロックをディスクに書き込む処理をOSに任せる
0

ファイルへの挿入は、潜在的に長期的な「高価な」影響と追加の障害モードを伴う「高価な」(時間を浪費し、スペースを消費する)操作と見なされるため、ほとんどのファイルシステムでは実装されていません。

挿入のセマンティクスを持つファイルシステムは、おそらくshift&insert(大きなファイルの前に挿入すると非常に高価になる可能性がありますが、長期的な副作用がほとんどない)か、ある種の一般化されたヒープ割り当てを使用します。可変長の割り当てサイズ(場合によっては非常に悪い動作のパフォーマンス[Stop-the-world GC中にファイルを保存しようとするインタラクティブユーザーの顔を想像してください!]).

実験したい場合は、JavaまたはPython挿入を実装する)でファイルI/O抽象化を簡単に構築できます。成功した場合そして、それは行儀の良いパフォーマンス特性を持ち、優れた研究論文の基礎を持っています。

0
Scott Leadley