C# 解析torrent文件

基础知识:

torrent文件信息存储格式: 

bencoding是一种以简洁格式指定和组织数据的方法。支持下列类型:字节串、整数、列表和字典。

1 字符串存储格式:  <字符串的长度>:<字符串的内容> 
例如:    4:abcd 表示abcd, 2:ab 表示ab

2 数字的存储格式:  i<整数>e
例如:    i32e 表示整数32, i1024e 表示整数1024

3 列表的存储格式: l<子元素>e  其中:子元素可以是字符串,整数,列表和字典,或者是它们的组合体
例如:    l4:asdf4:qwere    表示 [ "asdf", "qwer" ] 

4 字典的存储格式: d<<key><value><key><value><key><value>...<key><value>>e 
其中:key只能是字符串类型,value则可以是字符串,整数,列表和字典,或者是它们的组合体,key和value必须是成对出现的
例如:    d3:cow3:moo4:spam4:eggse    表示 { "cow" => "moo", "spam" => "eggs" } 
        d4:spaml1:a1:bee            表示 { "spam" => [ "a", "b" ] } 
        d9:publisher3:bob4:spaml1:a1:be5:counti80ee  表示 { "publisher" => "bob", "spam" => [ "a", "b" ], "count" => 80 }

代码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.IO;
  4 using System.Text;
  5 using NPOI.OpenXmlFormats.Spreadsheet;
  6 using Newtonsoft.Json;
  7 
  8 namespace ConsoleApp
  9 {
 10     /// <summary>
 11     /// Summary description for Class1.-change wdq
 12     /// </summary>
 13     public class Class1
 14     {
 15         static void Main(string[] args)
 16         {
 17             if (args.Length != 1)
 18             {
 19                 Console.WriteLine("请指定torrent文件");
 20                 return;
 21             }
 22             string filename = args[0];
 23             var data = Torrent.DecodeFile(filename);
 24             //Console.WriteLine(JsonConvert.SerializeObject(data, Newtonsoft.Json.Formatting.Indented));
 25             ShowData(data);
 26         }
 27 
 28         private static void ShowData(ItemBase data)
 29         {
 30             if (data.ItemType == ItemType.Dictionary)
 31             {
 32                 foreach (var kv in (data as DictionaryItem).DictionaryData)
 33                 {
 34                     switch (kv.Value.ItemType)
 35                     {
 36                         case ItemType.Dictionary:
 37                             Console.WriteLine(kv.Key + ":");
 38                             ShowData(kv.Value);
 39                             break;
 40                         case ItemType.List:
 41                             Console.WriteLine(kv.Key + ":");
 42                             ShowData(kv.Value);
 43                             break;
 44                         case ItemType.String:
 45                             if (kv.Key == "pieces")
 46                             {
 47                                 break;
 48                             }
 49                             Console.WriteLine(kv.Key + "=" + (kv.Value as StringItem).StringData);
 50                             break;
 51                         case ItemType.Number:
 52                             Console.WriteLine(kv.Key + "=" + (kv.Value as NumberItem).NumberData);
 53                             break;
 54                     }
 55                 }
 56             }
 57             if (data.ItemType == ItemType.List)
 58             {
 59                 foreach (var i in (data as ListItem).ListData)
 60                 {
 61                     switch (i.ItemType)
 62                     {
 63                         case ItemType.Dictionary:
 64                         case ItemType.List:
 65                             ShowData(i);
 66                             break;
 67                         case ItemType.String:
 68                             Console.WriteLine((i as StringItem).StringData);
 69                             break;
 70                         case ItemType.Number:
 71                             Console.WriteLine((i as NumberItem).NumberData);
 72                             break;
 73                     }
 74                 }
 75             }
 76         }
 77     }
 78 
 79     public class Torrent
 80     {
 81         public static ItemBase DecodeFile(string filename)
 82         {
 83             using (var fs = new FileStream(filename, FileMode.Open))
 84             {
 85                 using (BinaryReader br = new BinaryReader(fs))
 86                 {
 87                     return DecodeData(br);
 88                 }
 89             }
 90         }
 91 
 92         private static ItemBase DecodeData(BinaryReader br, Stack<bool> st = null)
 93         {
 94             var flag = br.PeekChar(); 
 95             List<byte> ls = new List<byte>();
 96             byte b = 0;
 97             switch (flag)
 98             {
 99                 case 'e':
100                     br.ReadByte();
101                     return null;
102                 case 'l'://列表
103                     br.ReadByte();
104                     var itemLs = new ListItem(); 
105                     ItemBase i = null;
106                     if (st == null)
107                     {
108                         st = new Stack<bool>();
109                     }
110                     st.Push(true);
111                     do
112                     {
113                         i = DecodeData(br, new Stack<bool>());
114                         if (i != null)
115                         {
116                             itemLs.ListData.Add(i);
117                         }
118                         else
119                         {
120                             st.Pop();
121                         }
122                     } while (st.Count != 0 && br.BaseStream.Position != br.BaseStream.Length);
123 
124                     return itemLs;                    
125                 case 'd'://字典
126                     br.ReadByte();
127                     var itemDic = new DictionaryItem();
128                     var key = DecodeData(br);
129                     while (key != null && br.BaseStream.Position != br.BaseStream.Length)
130                     {
131                         var val = DecodeData(br);
132                         itemDic.DictionaryData[(key as StringItem).StringData] = val;
133                         key = DecodeData(br);
134                     }
135 
136                     return itemDic; 
137                 case 'i'://数字
138                     br.ReadByte();
139                     b = br.ReadByte();
140                     while (b != 'e')
141                     {
142                         ls.Add(b);
143                         b = br.ReadByte();
144                     }
145                     return new NumberItem(long.Parse(Encoding.UTF8.GetString(ls.ToArray()))) { RawBytes = ls.ToArray() };                    
146                 default://字符串 
147                     b = br.ReadByte();
148                     while (b != ':')
149                     {
150                         ls.Add(b);
151                         b = br.ReadByte();
152                     }
153                     var len = int.Parse(Encoding.UTF8.GetString(ls.ToArray()));
154                     var bufStr = br.ReadBytes(len);
155                     var data = Encoding.UTF8.GetString(bufStr);
156                     return new StringItem(data) { RawBytes = bufStr };                    
157             } 
158         }
159     }
160 
161     public class ItemBase
162     {
163         [JsonIgnore]
164         public ItemType ItemType { get; set; }
165         [JsonIgnore]
166         public byte[] RawBytes { get; set; }
167     }
168 
169     public class StringItem : ItemBase
170     {
171         public StringItem(string data)
172         {
173             StringData = data;
174             ItemType = ItemType.String;
175         }
176         public string StringData { get; set; }
177     }
178     public class NumberItem : ItemBase
179     {
180         public NumberItem(long num)
181         {
182             NumberData = num;
183             ItemType = ItemType.Number;
184         }
185         public long NumberData { get; set; }
186     }
187 
188     public class ListItem : ItemBase
189     {
190         public ListItem()
191         {
192             ItemType = ItemType.List;
193         }
194         public List<ItemBase> ListData { get; set; } = new List<ItemBase>();
195     }
196 
197     public class DictionaryItem : ItemBase
198     {
199         public DictionaryItem()
200         {
201             ItemType = ItemType.Dictionary;
202         }
203         public Dictionary<string, ItemBase> DictionaryData { get; set; } = new Dictionary<string, ItemBase>();
204     }
205 
206     public enum ItemType
207     {
208         String, Number, List, Dictionary
209     }
210 }

Github地址:https://github.com/a14907/AConsoleAppForFun.git

  效果:

 

posted @ 2017-09-25 18:40  a14907  阅读(1791)  评论(0编辑  收藏  举报