web-dev-qa-db-ja.com

テキストエディターは通常どのように実装されますか?

この質問はおそらく私をかなり無知に聞こえるようにするつもりです。それは私がいるからです。

自分のテキストエディタのGUIコントロール、ウィジェット、またはそれを呼び出したいもの(私はそうではない)の設計に仮想的に興味がある場合はどうすればよいでしょうか。

私などの初心者の誘惑は、テキストエディターのコンテンツを文字列の形式で保存することです。これはかなりコストがかかるようです(言語/プラットフォーム間での文字列の実装の違いに精通しているわけではありません)。 ;しかし、たとえば.NETでは不変であるため、テキストエディターでサポートする必要があるような頻繁な操作は非常に無駄になり、文字列インスタンスを次から次へと非常に迅速に構築します)。

おそらく、テキストを含むいくつかの可変データ構造が代わりに使用されます。しかし、この構造がどのように見えるかを理解することは、ちょっとした挑戦として私を襲います。ランダムアクセスは良い(私はthinkとにかく—結局のところ、ユーザーがテキスト内のどこにでもジャンプできるようにしたくないですか?)が、それからコストについて疑問に思うたとえば、巨大なドキュメントの真ん中のどこかに移動して、すぐに入力を開始します。繰り返しますが、初心者のアプローチ(テキストをサイズ変更可能な文字の配列として保存するとします)は、パフォーマンスを非常に低下させます。ユーザーが入力するすべての文字には、「シフト」するための膨大な量のデータがあるからです。以上。

したがって、推測する必要がある場合、テキストエディタは、テキストを小さな部分(行など)に分割する何らかの構造を採用していると思います。これは、ランダムアクセスの文字配列を個別に含み、それら自体がランダムです。個別のチャンクとしてアクセスできます。でもthatは、最初から遠く離れていても、かなり巨大な単純化であるに違いないようです。

もちろん、テキストエディタを実装するためのbe "標準"の方法がない可能性があることも理解しています。多分それはエディターによって劇的に異なります。しかし、それは明らかに何度も取り組まれてきた問題であるため、おそらく比較的一般的なアプローチが何年にもわたって表面化していると思いました。

とにかく、このトピックについて誰かが知識を持っているかどうか知りたいだけです。先ほど言ったように、私は自分のテキストエディタを書くつもりはありません。気になるだけです。

53
Dan Tao

(特に古いエディターで)よく使われる手法の1つは、スプリットバッファーと呼ばれます。基本的に、テキストをカーソルの前のすべてとカーソルの後のすべてに「分割」します。以前のすべてがバッファの先頭になります。その後のすべてがバッファの最後になります。

ユーザーがテキストを入力すると、データは移動せずに、その間にある空白スペースに入ります。ユーザーがカーソルを移動すると、適切な量のテキストが「ブレーク」の片側から反対側に移動します。通常、1つの領域内を移動する回数が多いため、通常、一度に移動するのは少量のテキストだけです。最大の例外は、「行xxxに移動」という種類の機能がある場合です。

Charles Crowleyはより完全な トピックの議論 を書いています。 The Craft of Text Editing も参照してください。これは、分割されたバッファー(およびその他の可能性)をより深くカバーしています。

38
Jerry Coffin

しばらく前に、私はTclで独自のテキストエディターを作成しました(実際には、どこかからコードを盗み、認識できないほどに拡張しました。ああ、オープンソースの素晴らしさです)。

既に述べたように、非常に大きな文字列に対して文字列操作を実行すると、コストが高くなる可能性があります。そのため、エディタは改行ごとにテキストを小さな文字列に分割します( "\ n"または "\ r"または "\ r\n")。そのため、行レベルで小さな文字列を編集し、行間を移動するときにリスト操作を行うだけです。

これのもう1つの利点は、操作するのが単純で自然な概念であることです。私の頭の中では、改行が文法的にも構文的にも重要な長年のプログラミングによって、テキストの各行が個別に強化されているとすでに考えています。

また、テキストエディターの使用例がプログラマーエディターである場合にも役立ちます。たとえば、構文の強調表示を実装しましたが、Word /行の折り返しは実装していません。したがって、私の場合、テキストの改行と画面に描かれた線の間に1:1のマップがあります。

見たくなったら、ここに私のエディターのソースコードを示します。 http://wiki.tcl.tk/16056

ちなみにおもちゃではありません。 RAM(真剣に、どのようなテキストファイルですか?通常4〜5 MBの小説でさえ、 RAM。私は、ログファイルが数百MBに増加するのを見ただけです。

2
slebetman

一度にエディターに表示する必要があるテキストの量によっては、バッファーアプローチ全体に対して1つの文字列で十分な場合があります。私はメモ帳でこれができると思います-大きなファイルにテキストを挿入するのがどれほど遅いかに気づいたことはありますか?

ハッシュテーブルの行ごとに1つの文字列を持つことは、適切な妥協案のようです。特定の行へのナビゲーションと削除/貼り付けを、それほど複雑にすることなく効率的に行うことができます。

元に戻す機能を実装したい場合は、30回の変更でファイル全体の30コピーを保存せずに以前のバージョンに戻ることができる表現が必要ですが、ファイルが十分に小さければおそらく問題ありません。

1
Chris Smith

最も簡単な方法は、言語によって提供されるある種の文字列バッファクラスを使用することです。 charオブジェクトの単純な配列でさえ、ピンチになるでしょう。

テキストの追加、置換、シークは比較的高速です。もちろん、他の操作は時間がかかる可能性があります。もちろん、バッファの先頭に文字シーケンスを挿入することは、よりコストのかかるアクションの1つです。

ただし、これは、単純なユースケースでは、パフォーマンスに関して完全に許容できる場合があります。

挿入と削除のコストが特に大きい場合は、内部的にバッファーオブジェクトのリストを維持するバッファーラッパークラスを作成して最適化したいと思います。既存のバッファーの末尾で発生しなかったアクション(単純な置換を除く)は、関連するバッファーが関連するポイントで分割されるため、バッファーは末尾で変更できます。ただし、外側のラッパーは単純なバッファーと同じインターフェイスを維持するため、書き換える必要はありませんでした。私の検索アクション。

もちろん、この単純なアプローチは、非常に断片化されたバッファーですぐに終了します。適切な場合にバッファーを合体させる、またはたとえば、 1文字の挿入。多分ルールは、私が最大で2つの内部バッファーしか持っておらず、新しいバッファーを作成する前にそれらを合体するか、または何かが一度にバッファー全体のビューを要求した場合です。わからない。

重要なのは、私は単純に始めますが、慎重に選択されたインターフェイスを介して可変バッファにアクセスし、プロファイリングで必要なことがわかった場合は、内部実装を操作します。

ただし、I definitelyは不変のStringオブジェクトで開始することはできません!

1
Bill Michell