torrent文件的基本结构和语法这个不废话介绍了,一下为代码 ,复制即用,速度很快

package cn.p2p;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;import org.Yeah.FByteList;
import org.Yeah.FParse;
/** 集合:l...e *   字符串:length:...*  数字:i...e*/
public class TorrentFileParser extends FParse{private String pieceEncoding="ISO8859-1";private String announce;private List<String> trackers=new ArrayList<String>();private Date createdDate;private String comment;private String author;private List<TorrentFile> tfs=new ArrayList<TorrentFile>();private String encoding;//这个encoding似乎没啥意义,不是pieces的加密编码private FByteList bl=new FByteList(); private String website;private String fileName;private int pieceLength;private int AllPieceLength;private List<String> pieces=new ArrayList<String>();private String info;private String fileSize;public int fileLength() {int l=0;for(TorrentFile f:tfs) {l+=f.getLength();}int y=0;double j;if((y=l/1024)>=1 && (y=l/1024)<1024) {fileSize=(j=l/1024)+"KB";}else if((y=l/1024)>=1024 && (y=l/1024)<1024*1024) {fileSize=(j=l/1024)+"MB";}else {fileSize=(j=l/1024)+"GB";}return l;}public String toInfoHash() {StringBuffer sb=null;if(tfs.size()!=0) {sb=new StringBuffer("d5:files");sb.append("l");for(int i=0;i<tfs.size();i++) {sb.append("d6:lengthi"+tfs.get(i).getLength()+"e4:pathl"+tfs.get(i).getPath().length()+":"+tfs.get(i).getPath()+"ee");}sb.append("e").append("4:name"+fileName.length()+":"+fileName+"12:piece lengthi"+pieceLength+"e").append("6:pieces"+AllPieceLength+":");for(int j=0;j<pieces.size();j++) {sb.append(pieces.get(j));}sb.append("e");info=sb.toString();}return Stupid.getSha1(info, pieceEncoding);}private void readTorrentFile(String path) {try {FileInputStream fs=new FileInputStream(path);bl.read(fs, -1);//System.out.println(bl.detach().length);source=new StringBuffer(new String(bl.detach(),pieceEncoding));//读取完成,这里编码很重要 否则有些数据无法转换为相应的字符}catch(FileNotFoundException e) {System.out.println("找不到指定文件 "+path);e.printStackTrace();}catch(IOException e) {System.out.println("读取文件失败 "+path);e.printStackTrace();}}//@SuppressWarnings("unused")private void parse(String maker) {String value="";String len="";eatWhiteSpace();if(source.charAt(idx)!='d' && idx==0) {System.out.println("格式出错,种子格式应该是字典格式,请在种子文件首部加个 d,在种子文件是尾部加个e,并重试");return;}//表示进入字符串标记 //2 3 ... 解析字符串信息if(len=="" && source.charAt(idx)>=48 && source.charAt(idx)<=57) {while(source.charAt(idx)!=':') {len+=source.charAt(idx);idx++;}int l=Integer.parseInt(len);len="";idx++; if(maker=="") {for(int i=0;i<l;i++) {maker+=source.charAt(idx);idx++;}//这里结束后下标指向下一位parse(maker);//有了maker就直接递归,得到值 info就在这里进去 parse(files)}else if(maker!="") {//key和value必须都是stringif("pieces".equals(maker))AllPieceLength=l;for(int i=0;i<l;i++) {value+=source.charAt(idx);idx++;//这里结束后下标指向下一位}  switch(maker) {case "announce":announce=value;break;case "comment":comment=value;break;case "created by":author=value;break;case "encoding":encoding=value;break;case "website":website=value;break;case "announce-list":trackers.add(value);break;case "path":TorrentFile tf=tfs.get(tfs.size()-1);tf.setPath(value);break;case "name":fileName=value;break;case "pieces"://这里容易出问题try {byte[] g=value.getBytes(pieceEncoding);for(int i=0;i<g.length;i+=20) {byte[] j=new byte[20];System.arraycopy(g, i, j, 0, 20);String n=new String(j,pieceEncoding);pieces.add(n);} }catch(Exception e) {e.printStackTrace();}break;}}}else if(source.charAt(idx)=='i') {//数字idx++;//前进一步到数值位while(source.charAt(idx)!='e') {value+=source.charAt(idx);idx++;}switch(maker) {case "creation date":createdDate=new Date(Integer.parseInt(value));break;case "length":TorrentFile tf=new TorrentFile();tf.setLength(Integer.parseInt(value));tfs.add(tf);//添加一个filebreak;case "piece length":pieceLength=Integer.parseInt(value);break;}idx++;//进入下一位}else if(source.charAt(idx)=='l') {//这里一直把列表遍历完 4idx++;//前进一步到数值位switch(maker) {case "announce-list":while(source.charAt(idx)=='l') {idx++;//进入数据位parse(maker);//递归 出来是eidx++;//进入l}if(source.charAt(idx)=='e')//这个表示没有其他的l了idx++;break;case "files":while(source.charAt(idx)!='e') {parse(maker);}idx++;break;case "path":parse(maker);//e位idx++;//下一个e位break;}}else if(source.charAt(idx)=='d') {idx++;//进入数据位while(source.charAt(idx)!='e') {//从这里开始是关键parse("");}idx++;}}@Overridepublic String toString() {return "TorrentFileParser [announce=" + announce + ", trackers=" + trackers + ", createdDate=" + createdDate+ ", comment=" + comment + ", author=" + author + ", tfs=" + tfs + ", encoding="+ encoding + ", website=" + website + ", fileName=" + fileName+ ", pieceLength=" + pieceLength + ", pieces=" + pieces.size() + "]";}public void parseBT(String path) {readTorrentFile(path);parse("");}public static void main(String[] args) {TorrentFileParser t=new TorrentFileParser();t.parseBT("C:\\Users\\ff\\Desktop\\use.torrent");}/** **************************************/public String getPieceEncoding() {return pieceEncoding;}public void setPieceEncoding(String pieceEncoding) {this.pieceEncoding = pieceEncoding;}public String getAnnounce() {return announce;}public void setAnnounce(String announce) {this.announce = announce;}public List<String> getTrackers() {return trackers;}public void setTrackers(List<String> trackers) {this.trackers = trackers;}public Date getCreatedDate() {return createdDate;}public void setCreatedDate(Date createdDate) {this.createdDate = createdDate;}public String getComment() {return comment;}public void setComment(String comment) {this.comment = comment;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}public List<TorrentFile> getTfs() {return tfs;}public void setTfs(List<TorrentFile> tfs) {this.tfs = tfs;}public String getEncoding() {return encoding;}public void setEncoding(String encoding) {this.encoding = encoding;}public FByteList getBl() {return bl;}public void setBl(FByteList bl) {this.bl = bl;}public String getWebsite() {return website;}public void setWebsite(String website) {this.website = website;}public String getFileName() {return fileName;}public void setFileName(String fileName) {this.fileName = fileName;}public int getPieceLength() {return pieceLength;}public void setPieceLength(int pieceLength) {this.pieceLength = pieceLength;}public List<String> getPieces() {return pieces;}public void setPieces(List<String> pieces) {this.pieces = pieces;}public int getAllPieceLength() {return AllPieceLength;}public void setAllPieceLength(int allPieceLength) {AllPieceLength = allPieceLength;}public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}public String getFileSize() {return fileSize;}public void setFileSize(String fileSize) {this.fileSize = fileSize;}}

涉及到的其他代码

package org.Yeah;
//这是个-通用-的字符串解析类,提供基本的字符串操作方法
public class FParse extends FAttributeList{public StringBuffer source;protected int idx;protected char parsefenge;protected String parsename;protected String parsevalue;protected String tag;public boolean isWhiteSpace(char c){return ("\t\n\r ".indexOf(c)!=-1);}//是否到头了,是否遍历完了,是否没了。。。public boolean eof(){return(idx>=source.length());}//不是空格就returnpublic void eatWhiteSpace(){while(!eof()){if(!isWhiteSpace(source.charAt(idx)))return;idx++;}}//支持格式xxx=ccc 'xxx'='ccc' "xxx"="ccc"public void parseattributename(){if(parsename==null)parsename="";eatWhiteSpace();if(source.charAt(idx)=='\'' || source.charAt(idx)=='\"'){parsefenge=source.charAt(idx);idx++;while(source.charAt(idx)!=parsefenge){parsename+=source.charAt(idx);idx++;}idx++;}else{while(!eof()){//当没有分隔符,后面没有idx++,是因为循环已经把下标移到下一位了,而有分隔符时,循环只是把下标移到分隔符上。//碰到空格 等于号 大于号就停止解析if(isWhiteSpace(source.charAt(idx)) || source.charAt(idx)=='=' || source.charAt(idx)=='>')break;parsename+=source.charAt(idx);idx++;}}//坐标移到不是空格的地方,解决了有分隔号时后面有空格的问题,注意如果当前坐标不是空格,坐标是不变的eatWhiteSpace();}public void parseattributevalue(){if(parsevalue==null)parsevalue="";//这里存疑,待以后解决。if(parsefenge!=(char)0){return;}//这里就决定了name和value之间必须是等于号if(source.charAt(idx)=='='){//这里的光标是已经解析过name的光标idx++;eatWhiteSpace();if(source.charAt(idx)=='\'' || source.charAt(idx)=='\"'){parsefenge=source.charAt(idx);idx++;while(source.charAt(idx)!=parsefenge){parsevalue+=source.charAt(idx);idx++;}idx++;}else{//不要空格 不要大于号,这里的大于号,有些问题,因为前面//已经规定了必须是=号,不然代码根本到不了这里,这里的意义是省略大于号,为什么省略搞不清while(!eof() && !isWhiteSpace(source.charAt(idx)) && source.charAt(idx)!='>'){parsevalue+=source.charAt(idx);idx++;}eatWhiteSpace();}}}void addattribute(){//add是放在了本对象的ve当中,注意此类的继承结构FAttribute f=new FAttribute(parsename,parsevalue,parsefenge);add(f);}public StringBuffer getSource() {return source;}public void setSource(StringBuffer source) {this.source = source;}public int getIdx() {return idx;}public void setIdx(int idx) {this.idx = idx;}public char getParsefenge() {return parsefenge;}public void setParsefenge(char parsefenge) {this.parsefenge = parsefenge;}public String getParsename() {return parsename;}public void setParsename(String parsename) {this.parsename = parsename;}public String getParsevalue() {return parsevalue;}public void setParsevalue(String parsevalue) {this.parsevalue = parsevalue;}public String getTag() {return tag;}public void setTag(String tag) {this.tag = tag;}}
package org.Yeah;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;public class FByteList {//16位整数最大值(不是32位)=32768private final static int INITSIZE=32768; private byte buffer[] =new byte[INITSIZE];private int index=0;private int capacity(){return (buffer.length-index);}private void expand(){byte[] newbuffer=new byte[buffer.length*2];//源数组 源起始下标 目标数组 目标起始下标 复制的长度System.arraycopy(buffer, 0, newbuffer, 0, index);buffer=newbuffer;}public void read(InputStream in,int max) throws IOException{long l=0;int p=max;do{l=in.read(buffer,index,capacity());if(l<0)break;index+=l;//剩余的空间小于10时扩容if(capacity()<10)expand();if(max!=-1){max-=l;if(max<=0){//现已经解决for(int i = p;i<index;i++){buffer[i]=0;}break;}}}while(l!=0);}//大佬们都是复制 然后讲这个工具类的属性全部归0,方便以后使用public byte[] detach(){byte[] result=new byte[index];System.arraycopy(buffer, 0, result, 0, index);buffer=null;index=0;return result;}}

以上就是解析torrent涉及到的全部代码,读代码时注意递归。另外写几个感想:

关于编码,编码与解码使用的encoding要一致,否则数据对不上,在解码种子文件时最好不要使用UTF-8,这个编码会把某些字节数据给省略掉 如5,15等,这会导致原始种子文件和解析出来的String长度不一致,某些字节被去掉了,导致解析失败。

要想验证编码是否一致正确很简单,比较String.length和String.getBytes(encoding).length两个值。如果相等或后者是前者的两倍,把应该是一致的,否则就是不一致。这个时候更换 encoding知道满足一致的条件为止

递归和循环是两个遍历的解决方案,不要在同一个遍历对象上两个同时使用,很多情况下会产生冲突。使用递归时一定要用堆栈为参考来思考整个过程,否则很容易出错,尤其是在步骤较多的情况之下。

另外使用torrent文件下载文件可能在后续慢慢更新

我发现很多tracker要么连不上,要么给他发报文不回应。。。

用Java来解析torrent文件相关推荐

  1. Java 实现解析xml文件的基本步骤(做笔记)

    今天,初步学习了Java实现解析xml文件的基本步骤.不多说先上图解了. 源代码: public class Dome1 { public static void main(String[] args ...

  2. Java jdom解析xml文件带冒号的属性

    Java jdom解析xml文件带冒号的属性 转载请标明出处: https://dujinyang.blog.csdn.net/article/details/99644824 本文出自:[奥特曼超人 ...

  3. php解析torrent文件,PHP基于闭包思想实现的BT(torrent)文件解析工具实例详解

    本文实例讲述了PHP基于闭包思想实现的torrent文件解析工具.分享给大家供大家参考,具体如下: PHP对静态词法域的支持有点奇怪,内部匿名函数必须在参数列表后面加上use关键字,显式的说明想要使用 ...

  4. java处理解析xml文件的几种方法及每种方法的区别

    文章目录 了解xml xml文件的结构 **特殊处理** **CDATA** 强烈建议 处理xml文件的几种方式 认识Document对象 解析xml文档 该选择哪种方式解析 DOM解析xml文件 D ...

  5. java dat 解析_dat文件如何用java解析?

    用javamail解析邮件(失败 xiao__jia__jia3492018-11-03 如何用java输出Excel文件 wnk123456562012-07-19 Java发送邮件Excel附件名 ...

  6. Java当中解析ini文件对应到JavaBean当中

    目录 1.ini文件简介 2.ini文件 3.ini解析工具类 4.示例运行结果 1.ini文件简介 .ini 文件是Initialization File的缩写,即初始化文件,是windows的系统 ...

  7. Python3 解析 torrent 文件

    (可直接转到文末下载) 起因: 找资源的时候,在某些网站下载到torrent文件.虽说挺常见的,但是这玩意只能用第三方软件打开,总觉得不爽.另外也是单纯出于好奇心,想看看这玩意长什么样子,一探究竟. ...

  8. 微信小程序——利用java后台解析Excel文件的数据

    后台为java,ssm框架 1.在SpringMvc.xml文件中增加Spring文件上传的解析器 <bean id="multipartResolver"class=&qu ...

  9. java xsd 解析 xml文件_xsd解析xml

    下面讲述根据xml生成对应序列化反序列化类的过程,xml需要首先转化为xsd,然后再生成为实体类.其中,XSD是XML Schema Definition的缩写. 1.拥有一个xml文件 2.打开vs ...

  10. C# 解析torrent文件

    基础知识: torrent文件信息存储格式: bencoding是一种以简洁格式指定和组织数据的方法.支持下列类型:字节串.整数.列表和字典. 1 字符串存储格式:  <字符串的长度>:& ...

最新文章

  1. 描述一下Spring框架的作用和优点?
  2. DEAP:使用生理信号进行情绪分析的数据库(一、背景介绍与刺激选择)
  3. 《移动应用开发》实验报告——疫情地图
  4. CodeSmith 4.0 正式版发布
  5. java 多线程工具_多线程测试工具groboutils的使用
  6. Qt工作笔记-跑马灯效果
  7. 监听页面滚动触发事件,页面停止滚动触发事件
  8. iPhone 14进入代工试产阶段:首款打孔屏iPhone要来了
  9. python怎么读-Python怎么读?为什么叫Python?
  10. react 界面渲染完成 立即执行_React原理解析fiber、diff
  11. 考研高等数学第一讲手写笔记 函数、极限与连续
  12. Kali安装Nessus
  13. 独特性,就是你最好的竞争力
  14. 又一个好用的xbox360手柄驱动
  15. LeetCode K站中转内最便宜的航班(回溯法、动态规划)
  16. win10设置计算机关机时间,win10怎样固定时间关机_win10怎样设置电脑关机时间设置...
  17. MBTI职业性格测试完整版(静态题库)
  18. html5微信录音文件,微信H5录音实现
  19. SpannableStringBuilder: SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length关于edittext找不到输入值这个
  20. matlab中如何转动三维图_MATLAB小技巧之:绕任意空间轴旋转三维图形

热门文章

  1. 西北农林科技大学计算机导师,信息工程学院-西北农林科技大学
  2. YOLOv4: Optimal Speed and Accuracy of Object Detection
  3. 2021年CentOS7安装Oracle11g全记录
  4. NumLock键失灵
  5. 实验二 分析1996~2015年人口数据各个特征的分布与分散状况
  6. ThinkPad X230 后没有了Break 以及 Pause键,网工们囧了?
  7. ORCAD原理图检查
  8. vc语言c1083错误,vc++常见错误之二:“fatal error C1083: ”无法打开包括文件-Go语言中文社区...
  9. Golang开源流媒体服务器(RTMP/RTSP/HLS/FLV等协议)
  10. 注册表Regedit实现右键管理员权限运行notepad++打开文件