BencodeからXMLへの翻訳

こんにちは、Habr!

この投稿では、bencode形式のファイルをXMLファイルに変換できるアプリケーションを作成することをお勧めします。 C#を見てみましょう。

(はい、自転車を作成しようとします。なぜこれが必要なのですか?典型的な問題を解決できない場合、非標準の問題は解決しません)



ベンコード。 彼のように





だから。 ベンコード形式が「意味する」ものを見てみましょう。

含まれる(サポートする)4つのデータ型

  1. バイト文字列
  2. 整数
  3. 一覧
  4. 語彙


区切り文字として、BencodeはASCII文字と数字を受け入れます。

このデータはどのように含まれていますか?

私の意見では、単純なデータ型と複雑なデータ型を区別できます。

単純型:

1)文字列バイト-文字列の長さ(数値)、コロン記号( ":")、そして実際には文字列が含まれる前。 例:5:helloまたは12:hello、habr!

2)整数-記号「i」(整数)、数字、記号「e」(終了)として記述されます。 例:i9eまたはi199e。 また、このタイプは負の数を「サポート」します。 例i-9e(-9)を見てみましょう



複合データ型(単純型と複合型で構成される):

1)リスト-(別名配列)-順次書き込まれる他のbencodeタイプが含まれます。 記録方法は、記号「l」(リスト)、データ型の説明、記号「e」です。 例:l1:I3:You2:Wee [「I」、「You」、「We」]

2)辞書-(連想配列)-キーと値のデータが含まれます。 キーの形式では、常にバイトの文字列があり、データは「キー」フィールドによって辞書式順序でソートされます。 辞書は次のように定義されます-記号「d」(辞書)、キー値要素、記号「e」。 たとえば、d5:alpha4:beta10:filesCounti9ee ["alpha"-"beta"、 "filesCount"-9]



BEncodeはどこで使用されますか?




Bencodeは、お気に入りのすべての.torrentファイルで使用されています。 これらのファイルは、連想配列(辞書)を表します。

デバイスの.torrentファイルで停止しません。



データ構造を整理します




私の意見では、「棚にレイアウトする」ために、いくつかのクラスを持つことは論理的です。

したがって、単純な要素(BItem)のクラスを作成します(数値と文字列の両方を処理し、リストクラス(BList)を作成してから、クラス-辞書(BDictionary)を作成します。

BEncodeファイルを処理するとき、要素が互いにどのように続くのかわからないことを受け入れるため、要素(リスト、辞書、および単純なデータのすべてのクラスを操作するためのメソッドをカプセル化するクラス(BElement)を作成します。 複合クラスを取得します。

最後の5番目のクラスには要素のリストが含まれます(FileBEncoding)(1つの要素を作成できますが、より一般的なケースを取り上げましょう)。

グラフィカルには、次のようになります。





ファイル読み取りエンコーディング





BItemクラスには





/// <summary> /// integer value /// start - 'i' /// end - 'e' /// Example - i145e => 145 /// string value /// start - length /// end - /// Example 5:hello => "hello" /// </summary> public class BItem { protected string strValue = ""; protected int intValue = 0; protected bool IsInt = true; public bool isInt { get { return IsInt; } } public BItem(string A) { strValue = A; IsInt = false; } public BItem(int A) { IsInt = true; intValue = A; } public string ToString() { if (IsInt) return intValue.ToString(); return strValue; } }
      
      







BListクラスには





  /// <summary> /// List /// start - 'l' /// end - 'e' /// Example - l5:helloi145e => ("hello",145) /// </summary> public class BList { List<BElement> Items = null; public BList() { Items = new List<BElement>(); } public BElement this[int index] { get { if (Items.Count > index) { return Items[index]; } return new BElement(); } set { if (Items.Count > index) { Items[index] = value; } else { throw new Exception("   .   !"); } } } public int Count { get { return Items.Count; } } /// <summary> ///     /// </summary> public void Add(BElement inf) { Items.Add(inf); } }
      
      







BDictionaryクラスには



  /// <summary> /// Dictionary /// start - 'd' /// end - 'e' /// Example - d2:hi7:goodbyee => ("hi" => "goodbye") /// </summary> public class BDictionary { protected List<BElement> FirstItem = null; protected List<BElement> SecondItem = null; public BDictionary() { FirstItem = new List<BElement>(); SecondItem = new List<BElement>(); } public int Count{ get { return FirstItem.Count; } } /// <summary> ///  /// </summary> /// <param name="index"></param> /// <returns></returns> public BElement[] this[int index] { get{ if (FirstItem.Count > index) { BElement[] Items = new BElement[2]; Items[0] = FirstItem[index]; Items[1] = SecondItem[index]; return Items; } return new BElement[2]; } set{ if (FirstItem.Count > index) { FirstItem[index] = value[0]; SecondItem[index] = value[1]; } else { //FirstItem.Add(value[0]); // SecondItem.Add(value[1]); -    , ..    !!!!!     throw new Exception("   .   "); } } } /// <summary> ///    /// </summary> /// <param name="First"></param> /// <param name="Second"></param> public void Add(BElement First, BElement Second) { FirstItem.Add(First); SecondItem.Add(Second); } }
      
      







次に、「ユニバーサル」クラスのBElementに渡します。

BElementクラスには





  /// <summary> /// ""  /// </summary> public class BElement { public BItem STDItem = null; public BList LSTItem = null; public BDictionary DICItem = null; /// <summary> ///       string\integer /// </summary> /// <param name="Reader"> </param> /// <param name="CurrentCode">  </param> public void AddToBItem(StreamReader Reader, char CurrentCode) { char C; if (CurrentCode == 'i') {//  string Value= ""; C = (char)Reader.Read(); while (C != 'e') {// Value += C; C = (char)Reader.Read(); } try { int Res = Int32.Parse(Value); STDItem = new BItem(Res); } catch (Exception ex) { //   throw .     null' STDItem = null; } return; } int length = (int)CurrentCode - (int)'0'; C = (char)Reader.Read(); while (C != ':' && (C>='0' && C<='9')) { length = length * 10 + (int)C - (int)'0'; C = (char)Reader.Read(); } if (C!= ':') {//   (   ,   ,  throw new Exception("  "); //     throw     ...      =) STDItem = null; return; } string value = ""; for (int CurrentCount = 0; CurrentCount < length; CurrentCount++) { value += (char)Reader.Read(); } STDItem = new BItem(value); } /// <summary> /// . ,  l   /// </summary> /// <param name="Reader"> </param> public void AddToBList(StreamReader Reader) { LSTItem = new BList(); BElement Temp = GetNewBElement(Reader); while (Temp != null) { LSTItem.Add(Temp); Temp = GetNewBElement(Reader); } if (LSTItem.Count == 0) LSTItem = null;//  -        . } /// <summary> ///   /// </summary> /// <param name="Reader">  </param> public void AddToBDic(StreamReader Reader) { DICItem = new BDictionary(); BElement FirstTemp = GetNewBElement(Reader); BElement SecondTemp = GetNewBElement(Reader); while (FirstTemp != null || SecondTemp != null) { DICItem.Add(FirstTemp, SecondTemp); FirstTemp = GetNewBElement(Reader); SecondTemp = GetNewBElement(Reader); } if (DICItem.Count == 0) DICItem = null;//       } /// <summary> ///    .   /// </summary> /// <param name="Reader"> </param> /// <returns> </returns> public static BElement GetNewBElement(StreamReader Reader) { char C = (char)Reader.Read(); switch (C) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'i': {//   BElement STDElement = new BElement(); STDElement.AddToBItem(Reader, C); return STDElement; } case 'l': {// BElement LSTElement = new BElement(); LSTElement.AddToBList(Reader); return LSTElement; } case 'd': {// BElement DICElement = new BElement(); DICElement.AddToBDic(Reader); return DICElement; } default://("e") return null; } } }
      
      







最後のクラス、構造の「中心」-FileBEncoding

BEncode \ XMLライターアルゴリズムを実装します。



まず、読み取りを実装します。

このクラスに次のものを含めます。





  public class FileBEncoding { List<BElement> BenItems;//      ,   BElement BenItem BElement this[int index] { get { if (BenItems.Count > index) return BenItems[index]; return null; } set { if (BenItems.Count > index) { BenItems[index] = value; } else throw new Exception("  .   "); } } public FileBEncoding(string Path) { if (!File.Exists(Path)) return; BenItems = new List<BElement>(); StreamReader Reader = new StreamReader(Path, Encoding.ASCII); while (!Reader.EndOfStream) { BElement temp = BElement.GetNewBElement(Reader); if (temp != null) BenItems.Add(temp); } Reader.Close(); }
      
      







より明確な行出力





出力を既にxmlで表示したい場合は、この部分をスキップできます。

ここでは、情報の「構造化された」出力をファイルコンソールに提供したいと思います。

これには何が必要ですか?

C#のすべてのクラスは、クラスObjectから派生しています。 このクラスには、ToString()メソッドがあります。 デフォルトでは、このメソッドはタイプ名を表示します。 再定義します。



  private string BElementToSTR(BElement CurrentElement, int TabCount, bool Ignore = true) { //     if (CurrentElement == null) return "";//    ,       . string Result = "";//  if (Ignore)//      PasteTab(ref Result, TabCount); if (CurrentElement.STDItem != null) { Result += CurrentElement.STDItem.ToString(); return Result; } if (CurrentElement.LSTItem != null) {//  Result += "List{\n"; for (int i = 0; i < CurrentElement.LSTItem.Count; i++) Result += BElementToSTR(CurrentElement.LSTItem[i], TabCount + 1) + '\n'; PasteTab(ref Result, TabCount); Result += "}List\n"; return Result; } if (CurrentElement.DICItem != null) {//  Result += "Dict{\n"; for (int i = 0; i < CurrentElement.DICItem.Count; i++) { Result += BElementToSTR(CurrentElement.DICItem[i][0], TabCount + 1) +" => "+ BElementToSTR(CurrentElement.DICItem[i][1], TabCount+1,false) + '\n'; } PasteTab(ref Result, TabCount); Result += "}Dict\n"; return Result; } return "";//   null,     } private string PasteTab(ref string STR,int count) {// for (int i = 0; i < count; i++) STR += '\t'; return STR; } public string ToString() { string Result = ""; for (int i = 0; i < BenItems.Count; i++) { Result += BElementToSTR(BenItems[i], 0) + "\n\n"; } return Result; }
      
      







ここでは、追加の2つの関数を使用しました。 1つ目は単一のBElement(再帰的であることがわかります)を処理し、2つ目はインデントを作成します。



XMLファイルを作成する





コーディングを開始する前に、XML言語自体について少し説明したいと思います。

この言語は、情報交換の単一言語として広く使用されています。 C#(または.NETプラットフォーム)は、優れたXMLサポートを実装しています。 便利な作業のために、System.XML名前空間を接続して組み込みツールを使用します。



XMLドキュメントを作成するには、いくつかの方法があります。 私の選択はXmlWriterクラスでした。 このクラスを使用すると、オブジェクトを「最初から」作成できます。 そして、各要素と属性のレコードが順番にあります。 この方法の主な利点は、その高速性です。



ドキュメントを作成するには、2つのメソッドを定義します

void ToXMLFile(文字列パス)およびvoid BElementToXML(BElement Current、XmlWriter Writer、int order = 0)

最初のメソッドは、必要なXmlWriterクラスのオブジェクトを作成し、リストから要素ごとにBElementToXMLを呼び出します。

2番目の方法は、BElementの「ねじれを解く」(リスト\辞書の場合)に従事し、実際にはファイルを形成します。



  private void BElementToXML(BElement Current, XmlWriter Writer, int order = 0) { if (Current == null) return;//    if (Current.STDItem != null) {//     Writer.WriteAttributeString("STDType"+'_'+order.ToString(), Current.STDItem.ToString()); return; } if (Current.LSTItem != null) {// Writer.WriteStartElement("List");//    <List> for (int i = 0; i < Current.LSTItem.Count; i++) BElementToXML(Current.LSTItem[i],Writer,order);//  Writer.WriteEndElement();//  </List> return; } if (Current.DICItem != null) {// ( ) Writer.WriteStartElement("Dictionary"); for (int i = 0; i < Current.DICItem.Count; i++) { Writer.WriteStartElement("Dictionary_Items"); BElementToXML(Current.DICItem[i][0], Writer,order); BElementToXML(Current.DICItem[i][1], Writer,order+1); Writer.WriteEndElement(); } Writer.WriteEndElement(); return; } return; } public void ToXMLFile(string path) { using (XmlTextWriter XMLwr = new XmlTextWriter(path, System.Text.Encoding.Unicode)) { XMLwr.Formatting = Formatting.Indented; XMLwr.WriteStartElement("Bencode_to_XML"); foreach (BElement X in BenItems) { XMLwr.WriteStartElement("BenItem"); BElementToXML(X, XMLwr); XMLwr.WriteEndElement(); } XMLwr.WriteEndElement(); } }
      
      







その結果、BEncodeファイルを認識するより便利な方法が得られます。

(たとえば、ubuntuトレントファイルを使用しました。SHAはキーを削除しました。)



コンソール:(ToString()メソッド)





Excel:(xml)





おわりに





この短い記事では、BEncodeファイルを処理し、XMLファイルを生成するというレッスンを学びました。

また、BEncodeからXMLファイルを作成することは、BEncodeファイルのエディターを作成する場合などに役立ちます。 まとめたコードは次のとおりです



文学





All Articles