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
效果: