精华内容
下载资源
问答
  • 2021-02-26 13:57:12

    public static final String TYPE_JPG = "jpg";

    public static final String TYPE_GIF = "gif";

    public static final String TYPE_PNG = "png";

    public static final String TYPE_BMP = "bmp";

    public static final String TYPE_UNKNOWN = "unknown";

    /**

    * byte数组转换成16进制字符串

    * @param src

    * @return

    */

    public static String bytesToHexString(byte[] src){

    StringBuilder stringBuilder = new StringBuilder();

    if (src == null || src.length <= 0) {

    return null;

    }

    for (int i = 0; i < src.length; i++) {

    int v = src[i] & 0xFF;

    String hv = Integer.toHexString(v);

    if (hv.length() < 2) {

    stringBuilder.append(0);

    }

    stringBuilder.append(hv);

    }

    return stringBuilder.toString();

    }

    /**

    * 根据文件流判断图片类型

    * @param fis

    * @return jpg/png/gif/bmp

    */

    public static String getPicType(FileInputStream fis) {

    //读取文件的前几个字节来判断图片格式

    byte[] b = new byte[4];

    try {

    fis.read(b, 0, b.length);

    String type = bytesToHexString(b).toUpperCase();

    if (type.contains("FFD8FF")) {

    return TYPE_JPG;

    } else if (type.contains("89504E47")) {

    return TYPE_PNG;

    } else if (type.contains("47494638")) {

    return TYPE_GIF;

    } else if (type.contains("424D")) {

    return TYPE_BMP;

    }else{

    return TYPE_UNKNOWN;

    }

    } catch (IOException e) {

    e.printStackTrace();

    }finally{

    if(fis != null){

    try {

    fis.close();

    } catch (IOException e) {

    e.printStackTrace();

    }

    }

    }

    return null;

    }

    //test code

    System.out.println("图片格式1: " + getPicType(new FileInputStream(new File("D:\\3_副本.png"))));

    System.out.println("图片格式2: " + getPicType(new FileInputStream(new File("D:\\3.png"))));

    java 代码判断图片格式后缀名称

    /** * 图片判断 */ private static String getFormatName(Object o) { try { // Create an image input stream ...

    java中判断图片格式并且等比例压缩图片

    最近项目中需要判断上传的图片必须是png,jpg,gif三种格式的图片,并且当图片的宽度大于600px时,压缩图片至600px,并且等比例的压缩图片的高度. 具体的实现形式: 大致的思路是: 判断根据 ...

    &lbrack;改善Java代码&rsqb;使用forName动态加载类文件

    动态加载(Dynamic Loading)是指在程序运行时加载需要的类库文件,对Java程序来说,一般情况下,一个类文件在启动时或首次初始化时会被加载到内存中,而反射则可以在运行时再决定是否需要加载一 ...

    java代码实现图片处理功能。对图片质量进行压缩。

    java图片处理有点头疼,找了很多资料.在这里进行一个汇总,记录下个人的体验,也希望对大家有所帮助. 需求:浏览的图片需要在1M一下. 1.真正对图片的质量进行压缩的(不是通过修改图片的高,宽进行缩小 ...

    通过Java代码实现图片的放大和缩小

    本文介绍的例子在Android安卓手机上测试通过. 先看看效果吧.可以看到这个开发好的安卓应用有三个按钮:Zoom In缩小图片,Zoom Out放大图片和Save保存. 初始页面: 可以在左边边框自 ...

    如何使用Java代码给图片增加倒影效果

    效果 倒影率为90%时的效果: 倒影率10%时的效果: 实现原理 倒影率作为参数rate 传入Reflection button的事件处理函数: CreateImageWithReflection这个 ...

    如何通过Java代码判断当前的环境是否支持JRE 9

    JDK9已经出来有一段时间了,因此很多流行的Java应用纷纷增添了对JDK9乃至JDK10的支持,比如Tomcat. 我们通过这个链接下载最新的Tomcat源文件包,总共7MB: https://to ...

    java代码判断文件类型(判断文件后缀名)

    1.两点需要注意 1.string.spilt("\\.")分割字符串成子字符串数组,以“.”分割,必须写成string.spilt("\\.")的方式,不能写 ...

    用Java代码列出一个目录下所有的文件

    1.File类 File类在java.io.File包中,所以要导入这个包. File类中用到的方法: boolean isDirectory()       测试此抽象路径名表示的文件是否是个目录 ...

    随机推荐

    Web语义化

    在昨天和做SEO的同学聊了一会儿,当然我没有学会搜索引擎优化的技巧和知识,但在此之前一直对HTML5中header.footer.sidebar.article等标签嗤之以鼻,觉得这个和div没有什么 ...

    搭建SSH框架所需Jar包及其解释

    SSH2 ----struts2.1.8---- struts2-core-2.1.8.1.jar struts2核心包 struts2-json-plugin-2.1.8.1.jar struts2 ...

    hdu 4714

    一个树形dp的题,又是一个涉及不深的领域  = =: 不过在网上看到了大神用很巧的思路解决了这个题: 大神的思路就是: 从树的底部往上看:如果一棵子树拥有两个及以上的叶子节点,可以将这棵子树与大树分离 ...

    mysql编码和Java编码相应一览表

    MySQL to Java Encoding Name Translations MySQL Character Set Name Java-Style Character Encoding Name ...

    2015第23周四HTML特殊字符显示问题

    1.项目中常采用EL表达式来输出后台内容,但测试发现它并不能完美处理要输出内容包含有<>或&等HTML特殊字符问题.先直接给出此问题JSTL的解决方案: 1.引入标签:

    浅谈Log4j

    1 什么是Log4j Log4j 是Apache为Java提供的日志管理工具.为了你快速理解Log4j的作用,我们用下面的代码说明Log4j的作用.我们为了调试程序,总是需要在程序使用System.o ...

    走进 UITest for Xamarin&period;Forms

    上一篇  走进 Prism for Xamarin.Forms 讲了简单的创建一个项目,然后添加了几个页面来回切换,这篇想先搞下 UITest 官方详细地址:https://developer.xam ...

    Docker 创建ubuntu ,ssh,vnc 可连接

    **************************************************************************************************** ...

    svn 安装

    SVN简介: 为什么要使用SVN? 程序员在编写程序的过程中,每个程序员都会生成很多不同的版本,这就需要程序员有效的管理代码,在需要的时候可以迅速,准确取出相应的版本. Subversion是什么? ...

    谈谈mysql的悲观和乐观锁

    悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.之前有写过一篇文章关于并发的处理思路和解决方案,这里我单独将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍一 ...

    更多相关内容
  • 后缀树 & c++代码实现

    千次阅读 2018-11-10 23:49:24
    后缀树----构建 1.后缀树简介 后缀树是一种数据结构,一个具有m个字符的字符串S的后缀树T,就是一个包含一个根节点的有向树,该树恰好带有m+1个叶子,这些叶子被赋予从0到m的标号。每一个内部节点,除了根节点以外,...

    后缀树----构建

    1.后缀树简介

    后缀树是一种数据结构,一个具有m个字符的字符串S的后缀树T,就是一个包含一个根节点的有向树,该树恰好带有m+1个叶子,这些叶子被赋予从0到m的标号。每一个内部节点,除了根节点以外,都至少有两个子节点,而且每条边都用S的一个子串来标识。出自同一节点的任意两条边的标识不会以相同的字符开始。后缀树的关键特征是:对于任何叶子i,从根节点到该叶子所经历的边的所有标识串连起来后恰好拼出S 的从i位置开始的后缀,即S[I,…,m]。(这里有一个规定,即字符串中不能有空格,且最后一个字符不能与前面任何一个字符相同)

    为了方便理解概念,给出一个例子,下图是字符串"banana#"的后缀树。

    在这里插入图片描述

    2.算法设计

    源代码参见我的Github

    本文章利用的是Ukkonen算法构建后缀树,在 1995 年,Esko Ukkonen 发表了论文《On-line construction of suffix trees》,描述了在线性时间内构建后缀树的方法。 本文章参考数据结构系列——后缀树(附Java实现代码),理解之后做了适当的改进并用c++语言实现,代码在dev-cpp 5.11已测试。

    首先解释一下将要用到的几个概念:活动点(包括活动节点,活动边,活动长度)剩余后缀数。活动点中的活动节点:是用于查找一个后缀是否已经存在这棵树里,即查找的时候从活动节点的子节点开始查找,同时当需要插入边的时候也是插入到该节点下;而活动边则是每次需要进行分割的边,即成为活动边就意味着需要被分割;而活动长度则是指明从活动边的哪个位置开始分割。剩余后缀数是我们需要插入的后缀的数量,说明程序员点就是缓存的数量,因为每次如果要插入的后缀存在,则缓存起来。另外还用到了后缀字符数组,表明将要处理的后缀字符。

    对于指定的字符串,从前往后一次提取一个字符,将其加入后缀字符数组,然后剩余后缀数加一,在当前后缀树中(当然,刚开始树是空的)寻找是否存在当前字符的后缀,如果有,则继续进行循环读取下一个字符,如果没有,则进入后缀字符处理函数进行后缀处理,在后缀处理函数中,首先需要定位活动节点,然后在依据活动节点来进行不同的操作。

    那么,先来了解一下几个规则:

    规则一(活动节点为根节点时候的插入):

    o插入叶子之后,活动节点依旧为根节点;

    o活动边更新为我们接下来要更新的后缀的首字母;

    o活动长度减1;

    规则二(后缀链表):

    o每个阶段,当我们建立新的内部节点并且不是该阶段第一次建立内部节点的时候, 我们需要用指针从当前内部节点指向本阶段最近一次建立的内部节点。

    规则三(活动节点不为根节点时候的插入):

    o如果当前活动节点不是根节点,那么我们每次从活动节点新建一个叶子之后,就要沿着后缀链表到达新的节点,并更新活动节点,如果不存在后缀链表,我们就转移到根节点,将活活动节点更新为根节点但活动长度以及活动边不变。

    额外规则(活动点的晋升)

    o如果活动边上的所有字符全部都被匹配完了(即活动边上的字符数==活动长度),则将活动边连接的下一个节点晋升为活动节点,同时重置活动长度为0。

    也就是说更新活动点后,如果活动节点是根节点则按照规则一进行处理,如果活动节点不是根节点,则按照规则三进行处理,在处理过程中,还要时刻注意规则二和额外规则。当新建节点时,遵循以下规则,如果新建时,活动边存在,则分裂活动边,分割的位置由活动长度指定;如果活动边不存在,则就在活动节点下新建节点和边。

    3.模块描述

    (1)数据类型

    首先定义结构变量及类,包括Node结构体,Edge结构体,ActivePoint结构体,以及SuffixTree类。

    Node结构体----后缀树中的节点

    struct Node
    {
    	int flag;
    	int count;//链接的边的个数,用下边的边指针数组存储 
    	Edge *child[max];
    	Edge *parent;
    	Node *next;//后缀链接标识 
    	Node(){flag=-1;parent=NULL;count=0;next=NULL;}
    	Node(int f){flag=f;parent=NULL;count=0;next=NULL;}
    };
    

    Edge结构体----后缀树中的边

    struct Edge
    {
    	string str;
    	Node *above,*below;//head-->above   back--->below 
    	Edge(){str="";above=NULL;below=NULL;}
    	Edge(Node *above,Node *below,string str)
    	{
    		this->str=str;
    		this->above=above;
    	    this->below=below;
    		this->above->child[above->count++]=this;
    		this->below->parent=this;
    	}
    	Edge(Node *above,int i,Node *below,string str)
    	{
    		this->str=str;
    		this->above=above;
    		this->below=below;
    		this->above->child[i]=this;
    		this->below->parent=this;
    	}
    };
    

    ActivePoint结构体----活动点

    struct ActivePoint
    {
    	Node *node;//活动节点 
    	Edge *edge;//活动边 
    	int length;//活动长度 
    	ActivePoint(){node=NULL;edge=NULL;length=0;}
    	ActivePoint(Node*n,Edge*e,int len){node=n;edge=e;length=len;}
    };
    

    SuffixTree类----后缀树类

    class SuffixTree
    {
    	public:
    		SuffixTree()
    		{
    			root=new Node();
    			activepoint=new ActivePoint(root,NULL,0);
    			reminder=0;
    			helpstr="";
    			suffixarray="";
    			active=NULL;
    		}
    		~SuffixTree(){delall(root);} //析构函数 
    		void delall(Node *p);//实际释放空间函数,释放节点p的所有孩子 (从后往前)
    		
    		int getlength(Node *p);//从p节点向上到根节点经历过的边的字符个数 
    		string getstr(Node *node);//从根节点向下到p节点拼出字符串 
    		string getallstr(){return helpstr;}//返回该树的字符串 
    		
    		bool search(Node *p,string str,Node *&cur);//从p节点向下寻找与字符串str匹配的,找到返回true 
    		bool findstr(string str);//查找字符串是否存在 
    		string findlongeststr();//寻找最长重复字符串 
    		void finddeepestr(Node *a[],Node *p,int &cal);//寻找每个分支的最长重复字符串 
    		
    		int count(string str);//计算字符串str出现的次数 
    		int countleaf(Node *p);//计算p节点下的叶节点个数 
    		bool judgeleaf(Node *p);//判断p节点先是否全为叶节点 
    		
    		int find(char x);//查找指定的后缀是否存在 
    		void build(string str);//构建后缀树 
    		void deal(string str,int currentindex);//处理后缀函数 
    		
    		void showtree(){show(root,0,0);}//打印后缀树 
    		void show(Node *p,int repeat,int len);//打印后缀树实际函数 
    		
    		void test()//测试用函数,展示当前活动点,后缀字符,剩余后缀数等信息 
    		{
    			if(activepoint->edge!=NULL)
    			{
    				cout<<"\n apnode="<<getstr(activepoint->node)<<",apedge="<<activepoint->edge->str<<",aplen="<<activepoint->length;
    		    	cout<<",reminder="<<reminder<<",suffixarray="<<suffixarray<<"\n";
    			}
    			else
    			{
    				cout<<"\n apnode="<<getstr(activepoint->node)<<",apedge=NULL,aplen="<<activepoint->length;
    		    	cout<<",reminder="<<reminder<<",suffixarray="<<suffixarray<<"\n";
    			}
    		}
    	private:
    		Node *root;
    		ActivePoint *activepoint;
    		int reminder;
    		string suffixarray;	
    		Node *active;
    		string helpstr;	
    };
    

    (2)算法描述

    build(String word):在SuffixTree中定义一个build(String word)方法,是后缀树构建的入口函数。首先依次提取字符串的每个字符,并按照算法步骤逐个插入。find(char w)用于查找指定的后缀是否存在(这里所说的后缀其实就是单个字符,因为单个字符代表的就是以该字符开头的后缀)。如果当前后缀未找到,就进入后缀字符处理函数deal(),如果找到,就继续循环。build()源代码如下,

    /****************************************************************************
    **build(string str)方法: 
    **以str构造后缀树 
    ****************************************************************************/
    void SuffixTree::build(string str)
    {
    	helpstr=str;
    	int index=0;
    	Edge *&apedge=activepoint->edge;
    	Node *&apnode=activepoint->node; 
    	int &aplen=activepoint->length;
    	
    	while(index<str.length())
    	{
    		//cout<<"\n当前处理的: "<<index<<","<<str[index]<<"\n";
    		//test();cout<<"\n" ;
    		int currentindex=index++;
    		char w=str[currentindex];
    		
    		//查找是否存在保存有当前后缀字符的节点 
    		if(find(w)!=-1)//如果找到了 
    		{
    			suffixarray+=w;
    			reminder++;
    			continue;
    		}
    		else //如果未找到 
    		{
    			suffixarray+=w;
    			reminder++;
    	    }
    		active=NULL;
    		deal(str,currentindex);
    		
    	}
    }
    

    find():查找后缀是否存在是从活动边开始查找,如果活动边为NULL,则从活动节点的子节点挨个查找,查找是通过比较边上的指定位置(活动长度指定)与查找字符是否相等。这里有个地方需要注意:算法中提到,如果一个活动边已到达结尾(即活动长度==活动边的字符长度),则将活动边晋升为活动节点,并重置活动边和活动长度为NULL和0。

    /****************************************************************************
    **int find(char x)方法: 
    **查找当前后缀是否存在,不存在返回-1 
    ****************************************************************************/
    int SuffixTree::find(char x)
    {
    	Edge *&apedge=activepoint->edge;
    	Node *&apnode=activepoint->node;
    	int &aplen=activepoint->length;
    	if(apedge==NULL)
    	{//无活动边,则从活动节点的子节点开始查找
    		for(int i=0;i<apnode->count;i++)
    		{
    			//cout<<i;
    			Edge *tempedge=apnode->child[i];
    		    if(tempedge->str[0]==x)
    		    {	  
    		        aplen++;
    		        apedge=apnode->child[i];
    		        if(aplen==apedge->str.length())
    	            {//这里出现了一个修改活动点的规则:即如果活动边上的所有字符全部都被匹配完了
    			     //(级活动边上的字符数==活动长度),则将活动边晋升为活动点,同时重置活动长度为0。
    			     //所以下次查找时就得从该节点开始了,而不是根节点了。
    			
    			        apnode=apedge->below;
    	    	        aplen=0;
    	    	        apedge=NULL;
    	    	        //return 1;
    		        }
    		    	return i;
    			}
    		       
    		}
    		return -1;
    	}
    	else
    	{// 有活动边,则在活动边上查找
    	    if(apedge->str[aplen]==x)
    	    {
    	    	aplen++;
    	        if(aplen==apedge->str.length())
    	        {//这里出现了一个修改活动点的规则:即如果活动边上的所有字符全部都被匹配完了
    			 //(级活动边上的字符数==活动长度),则将活动边晋升为活动点,同时重置活动长度为0。
    			 //所以下次查找时就得从该节点开始了,而不是根节点了。
    			
    			    apnode=apedge->below;
    	    	    aplen=0;
    	    	    apedge=NULL;
    		    }
    		    return 1;
    	    }
    	    else
    	        return -1;
    		    
    	}
    	return -1;
    }
    

    deal():该方法是用来处理后缀字符的,也是后缀树构建中的主要部分,主要就是依据上文提到的几个规则来进行,deal()源代码如下,

    /****************************************************************************
    **deal(string str,int currentindex,int number)方法: 
    **处理后缀字符,str是输入的字符,currentindex是处理到的位置,number表示本次操作
    **使用了几次后缀链表 
    ****************************************************************************/
    void SuffixTree::deal(string str,int currentindex)
    {
        //cout<<"\n----------------------------------------------\n";
    	//cout<<"deal函数入口:\n";
       // test();show(root,0,0);
        
    
    	Edge *&apedge=activepoint->edge;
    	Node *&apnode=activepoint->node;
    	int &aplen=activepoint->length;
    	if(reminder==1)//如果剩余后缀数为1 //pay attention to//是否一定为根,当reminder为1的时候 
    	{
    		if(apnode==root)//如果活动节点是根节点 
    		{//新建节点 
    			Node *tempnode1=new Node(currentindex-suffixarray.length()+1);
    			Edge *tempedge1=new Edge(apnode,tempnode1,str.substr(currentindex));
    			suffixarray.erase(0,1);
    			reminder--;
    			apedge=NULL;
    			
    			//cout<<"deal函数出口:\n";
                //test();show(root,0,0);
                //cout<<"\n----------------------------------------------\n";
    			return;
    		}
    		else//如果活动节点不是根节点,apnode!=root 
    		{
    			
    		}
    	}
    	else//剩余后缀数大于1
    	{
    		if(apnode==root)
    		{
    				//规则一(活跃点为根节点时候的插入):
                    //o插入叶子之后,活跃节点依旧为根节点;
                    //o活跃边更新为我们接下来要更新的后缀的首字母; 
                    //o活跃半径减1;
                    if(apedge==NULL)//如果活动边不存在,即说明活动节点下需要新创建节点
    				{
    					Node *tempnode1=new Node(currentindex);
    					Edge *tempedge1=new Edge(apnode,tempnode1,str.substr(currentindex));
    					//活动边依旧设置为空 		
    				} 
    				else
    				{
    						    Edge *edge=apedge;//保存当前活动边,也便于后边释放旧有的活动边 
    						    apedge=NULL;
    						    aplen--;//因为一定能找到,因此寻找过程中会使得aplen++,此处修正
    							int m=find(edge->str[0]);//寻找标号,后边新建节点会用到 
    							
    							 
    							Node *tempnode1=new Node();
    					        Edge *tempedge1=new Edge(tempnode1,apedge->below,apedge->str.substr(aplen));
    					        Edge *tempedge2=new Edge(apnode,m,tempnode1,apedge->str.substr(0,aplen));
    					        
    							Node *tempnode2=new Node(currentindex-suffixarray.length()+1);
    							Edge *tempedge3=new Edge(tempnode1,tempnode2,str.substr(currentindex));
    							
    				 			apedge=apnode->child[m];
    							delete edge;//释放旧有的活动边 
    								 
    					
    				    
    			 	    
    				}
                    
    				
    				
    				//规则二(后缀链表):
                    //o每个阶段,当我们建立新的内部节点并且不是该阶段第一次建立内部节点的时候,
                    //我们需要用指针从当前内部节点指向本阶段最近一次建立的内部节点。 
      
                    //如果当前新建节点是内部节点,则更新后缀链表 
                    if(apedge!=NULL&&apedge->below->count>1)
    				{
    					if(active==NULL)
    				        active=apedge->below;
    				    else
    				    {
    					    active->next=apedge->below;
    					    active=apedge->below;
    				    }
    				}
    				else if(apedge==NULL)
    				{
    					if(active==NULL)
    				        active=apnode;
    				    else
    				    {
    					    active->next=apnode;
    					    active=apnode;
    				    }
    				}
    				
    				
    				
    				suffixarray.erase(0,1);
    				reminder--;
    				aplen--;
    				
    				
    				apedge=NULL;//apnode已经为空 
    				aplen=0;
    			    int flag;
    				for(int i=0;i<reminder;i++)
    				{
    					flag=find(suffixarray[i]);
    			    }
    			    
    			    
    			    if(flag==-1)
    			    {
    			    	//cout<<"deal函数出口:\n";
                        //test();show(root,0,0);
                        //cout<<"\n----------------------------------------------\n";
    			    	deal(str,currentindex);
    			    	return;
    				}
    				else
    				{
    					//cout<<"deal函数出口:\n";
                        //test();show(root,0,0);
                        //cout<<"\n----------------------------------------------\n";
    					return;
    				}
    						
    					
    				
    		}
    		else//apnode!=root
    		{
    			    //规则三(活跃点不为根节点时候的插入):
                    //o如果当前活跃点不是根节点,那么我们每次从活跃点新建一个叶子之后,
                    //就要沿着后缀链表到达新的节点,并更新活跃节点,如果不存在后缀链表,
                    //我们就转移到根节点,将活跃节点更新为根节点但活跃半径以及活跃边不变。
                    
                    char temp;
    				if(apedge==NULL)//如果活动边不存在,即说明活动节点下需要新创建节点
    				{
    					Node *tempnode1=new Node(currentindex-suffixarray.length()+1);
    					Edge *tempedge1=new Edge(apnode,tempnode1,str.substr(currentindex));
    					//这个时候活动节点怎么定义???? //依旧当做新建的内部节点处理 	
    				}
    				else 
    				{
    					
    					
    					    Edge *edge=apedge;
    					    temp=edge->str[0];
    					    apedge=NULL;
    					    aplen--;
    						int m=find(edge->str[0]);
    						
    						
    						Node *tempnode1=new Node();
    						//cout<<"what happened?\n";//这里曾经出现一个问题就是前面的"aplen--"与"int m=find(edge->str[0])"顺序错误而产生的问题 
    						//cout<<apedge->str<<" "<<aplen;//当顺序反后,活动点可能会错误的升级,而这不是我想要的 
    						//cout<<apedge->str.substr(aplen)<<"\n";
    						Edge *tempedge1=new Edge(tempnode1,apedge->below,apedge->str.substr(aplen));
    					    Edge *tempedge2=new Edge(apnode,m,tempnode1,apedge->str.substr(0,aplen));
    					        
    						Node *tempnode2=new Node(currentindex-suffixarray.length()+1);
    						Edge *tempedge3=new Edge(tempnode1,tempnode2,str.substr(currentindex));
    						
    						apedge=apnode->child[m];	
    						delete edge;	
    				} 
    				reminder--;
    				suffixarray.erase(0,1);
    				
    				 //如果当前新建节点是内部节点,则更新后缀链表 
                    if(apedge!=NULL&&apedge->below->count>1)
    				{
    					if(active==NULL)//注意加判定以判断是否为内部节点!!! 
    				        active=apedge->below;
    				    else
    				    {
    					    active->next=apedge->below;
    					    active=apedge->below;
    				    }
    				}
    				else
    				{
    					if(active==NULL)//注意加判定以判断是否为内部节点!!! 
    				        active=apnode;
    				    else
    				    {
    					    active->next=apnode;
    					    active=apnode;
    				    }
    				
    				}
    				
    
    				//开始沿着后缀链表寻找,并且重置活动点 
    				if(apnode->next!=NULL)//如果有连接,就进入 
    				{
    					
    						apnode=apnode->next;
    					    apedge=NULL;
    					    int tempaplen=aplen;
    					    aplen=0;
    					    
    					   
    				        int flag;
    			            for(int i=reminder-tempaplen-1;i<reminder;i++)
    				        {
    					     flag=find(suffixarray[i]);
    			            }
    			            
    			            
    			            
    			            if(flag==-1)
    			            {
    			            	//cout<<"deal函数出口:\n";
                                //test();show(root,0,0);
                                //cout<<"\n----------------------------------------------\n";
    			    	        deal(str,currentindex);
    			    	        return;
    			           	}
    			        	else
    			        	{
    			        		//cout<<"deal函数出口:\n";
                                //test();show(root,0,0);
                                //cout<<"\n----------------------------------------------\n";
    					        return;
    				        }
    					 
    					
    				}
    				else//如果当前节点无连接,就置活动节点为根节点 
    				{
    					apnode=root;
    					apedge=NULL;
    					aplen=0;
    				    int flag;
    	    		    for(int i=0;i<reminder;i++)
    				    {
    					    flag=find(suffixarray[i]);
    			        }
    			        if(flag==-1)
    			        {
    			        	//cout<<"deal函数出口:\n";
                            //test();show(root,0,0);
                            //cout<<"\n----------------------------------------------\n";			        	
    			    	    deal(str,currentindex);
    			    	    return;
    			        }
    			        else
    			        {
    			        	//cout<<"deal函数出口:\n";
                           //test();show(root,0,0);
                            //cout<<"\n----------------------------------------------\n";			        	
    					    return;
    				    }
    					
    					
    				}
    		}//apnode!=root终结 
    		
    	}//reminder>1终结 
    	    
    } 
    
    展开全文
  • 当把文件的二进制数据转换成十六进制时,同类型文件的文件头数据是相同的,即使改变了其后缀,这个数据也不会改变。文件头是位于文件开头的一段承担一定任务的数据,一般在开头部分。头文件作为一种包含功能函数、...

    目录

    前言

    一、如何获取真实文件格式?

    二、发开步骤

    1. 定义枚举类,建立常见的文件类型与文件头魔数对应关系

    2.新建GetFileTypeUtil工具类,获取文件格式

    3.使用方法

    总结


    前言

     今天在做文件上传下载的功能,前端需要我把文件转成base64编码给她。但是发现Java base64转码会缺失data数据,这样也就无法识别该文件是什么类型的,于是想写个工具类获取文件类型,追加上去。开始是想根据文件后缀名判断的,但这样要是故意修改文件后缀就无法准确判断了。在咨询一番度娘后,决定采用读取文件的十六进制文件头来判断文件的真正类型。


    提示:以下是本篇文章正文内容,下面内容仅供个人学习记录,欢迎评论交流学习

    一、如何获取真实文件格式?

         当把文件的二进制数据转换成十六进制时,同类型文件的文件头数据是相同的,即使改变了其后缀,这个数据也不会改变。文件头是位于文件开头的一段承担一定任务的数据,一般在开头部分。头文件作为一种包含功能函数、数据接口声明的载体文件,用于保存程序声明(declaration),而定义文件用于保存程序的实现(implementation)。一般文件头开始几节数据是固定的,被称为魔数,作为区分文件类型的硬添加进去的数据。(例如PNG格式的十六进制文件头数据就是以"89504E47"开头的,就可以判定该文件是PNG格式)为了解决在用户上传文件的时候在服务器端判断文件类型的问题,故用获取文件头的方式,直接读取文件的前几个字节,来判断文件的真实类型。

     

    二、发开步骤

    1. 定义枚举类,建立常见的文件类型与文件头魔数对应关系

    代码如下(示例):

    package com.ruoyi.vip.fileLearn;
    
    public enum FileType {
        /**
         * JEPG.
         */
        JPEG("FFD8FF"),
    
        /**
         * PNG.
         */
        PNG("89504E47"),
    
        /**
         * GIF.
         */
        GIF("47494638"),
    
        /**
         * TIFF.
         */
        TIFF("49492A00"),
    
        TXT("6C657420"),
    
        /**
         * Windows Bitmap.
         */
        BMP("424D"),
    
        /**
         * CAD.
         */
        DWG("41433130"),
    
        /**
         * Adobe Photoshop.
         */
        PSD("38425053"),
    
        /**
         * Rich Text Format.
         */
        RTF("7B5C727466"),
    
        /**
         * XML.
         */
        XML("3C3F786D6C"),
    
        /**
         * HTML.
         */
        HTML("68746D6C3E"),
    
        /**
         * Email [thorough only].
         */
        EML("44656C69766572792D646174653A"),
    
        /**
         * Outlook Express.
         */
        DBX("CFAD12FEC5FD746F"),
    
        /**
         * Outlook (pst).
         */
        PST("2142444E"),
    
        /**
         * MS Word/Excel.
         */
        XLS_DOC("D0CF11E0"),
    
        /**
         * MS Access.
         */
        MDB("5374616E64617264204A"),
    
        /**
         * WordPerfect.
         */
        WPD("FF575043"),
    
        /**
         * Postscript.
         */
        EPS("252150532D41646F6265"),
    
        /**
         * Adobe Acrobat.
         */
        PDF("255044462D312E"),
    
        /**
         * Quicken.
         */
        QDF("AC9EBD8F"),
    
        /**
         * Windows Password.
         */
        PWL("E3828596"),
    
        /**
         * ZIP Archive.
         */
        ZIP("504B0304"),
    
        /**
         * RAR Archive.
         */
        RAR("52617221"),
    
        /**
         * Wave.
         */
        WAV("57415645"),
    
        /**
         * AVI.
         */
        AVI("41564920"),
    
        /**
         * Real Audio.
         */
        RAM("2E7261FD"),
    
        /**
         * Real Media.
         */
        RM("2E524D46"),
    
        /**
         * MPEG (mpg).
         */
        MPG("000001BA"),
    
        /**
         * Quicktime.
         */
        MOV("6D6F6F76"),
    
        /**
         * Windows Media.
         */
        ASF("3026B2758E66CF11"),
    
        GZ("1F8B08"),
        /**
         * MIDI.
         */
        MID("4D546864");
    
        private String value = "";
    
    
        /**
         * Constructor.
         * @param value
         */
        private FileType(String value) {
            this.value = value;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    }
    

    2.新建GetFileTypeUtil工具类,获取文件格式

    代码如下(示例):

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * @description 判断文件类型工具类
     * @author 13192
     */
    public class GetFileTypeUtil {
    
    
        /**
         * @description 第一步:获取文件输入流
         * @param filePath
         * @throws IOException
         */
        private static String getFileContent(String filePath) throws IOException {
    
            byte[] b = new byte[20];
            InputStream inputStream = null;
            try {
                inputStream = new FileInputStream(filePath);
                /**
                 * int read() 从此输入流中读取一个数据字节。int read(byte[] b) 从此输入流中将最多 b.length
                 * 个字节的数据读入一个 byte 数组中。 int read(byte[] b, int off, int len)
                 *从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
                 * 之所以从输入流中读取20个字节数据,是因为不同格式的文件头魔数长度是不一样的,比如 EML("44656C69766572792D646174653A")和GIF("47494638")
                 * 为了提高识别精度所以获取的字节数相应地长一点
                 */
                inputStream.read(b, 0, 20);
            } catch (IOException e) {
                e.printStackTrace();
                throw e;
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        throw e;
                    }
                }
            }
            return bytesToHexString(b);
        }
        /**
         * @description 第二步:将文件头转换成16进制字符串
         * @param
         * @return 16进制字符串
         */
        private static String bytesToHexString(byte[] src){
            StringBuilder stringBuilder = new StringBuilder();
            if (src == null || src.length <= 0) {
                return null;
            }
            for (int i = 0; i < src.length; i++) {
                int v = src[i] & 0xFF;
                String hv = Integer.toHexString(v);
                if (hv.length() < 2) {
                    stringBuilder.append(0);
                }
                stringBuilder.append(hv);
            }
            System.out.println("文件类型16进制字符串是"+stringBuilder.toString());
            return stringBuilder.toString();
        }
        /**
         * @description 第三步:根据十六进制字符串判断文件类型格式
         * @param filePath 文件路径
         * @return 文件类型
         */
        public static FileType getType(String filePath) throws IOException {
            String fileHead = getFileContent(filePath);
            if (fileHead == null || fileHead.length() == 0) {
                return null;
            }
            fileHead = fileHead.toUpperCase();
            FileType[] fileTypes = FileType.values();
            for (FileType type : fileTypes) {
    //            startsWith() 方法用于检测字符串是否以指定的前缀开始
                if (fileHead.startsWith(type.getValue())) {
                    return type;
                }
            }
            return null;
        }
    }

    3.使用方法

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.util.HashMap;
    
    /**
     * @author : MasterWei
     * @date : 2020-12-10 21:53
     * @description :
     * @params :
     * @return :
     **/
    public class test {
        public static void main(String[] args) throws Exception {
            //故意把png文件的后缀改成txt,看是否能获取到真实文件格式
            final String filePath = getFileType("C:\\Users\\13192\\Pictures\\123.png.txt");
            String name=GetFileTypeUtil.getType(filePath).name();
            System.out.println("文件格式是"+name);
        }


    总结

    本文简单示例了Java如何获取真实文件格式的小demo,其中FileType枚举类可以在实际应用中添加更多对应的魔数,以增强识别读。

    展开全文
  • 示例代码: var jsonArray = [  { "user": { "id": 100, "screen_name": "d_linq" }, "text": "to objects" },  { "user": { "id": 130, "screen_name": "c_bill" }, "text": "g" },  { "user": { ...

    相关知识:

    1、substr()的定义和用法

    substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。

    语法

    stringObject.substr(start,length)
    参数描述
    start必需。要抽取的子串的起始下标。必须是数值。如果是负数,那么该参数声明从字符串的尾部开始算起的位置。也就是说,-1 指字符串中最后一个字符,-2 指倒数第二个字符,以此类推。
    length可选。子串中的字符数。必须是数值。如果省略了该参数,那么返回从 stringObject 的开始位置到结尾的字串。

    返回值

    一个新的字符串,包含从 stringObject 的 start(包括 start 所指的字符) 处开始的 length 个字符。如果没有指定 length,那么返回的字符串包含从 start 到 stringObject 的结尾的字符。

    2、lastIndexOf()的定义和用法

    lastIndexOf() 方法可返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。

    语法

    stringObject.lastIndexOf(searchvalue,fromindex)
    参数描述
    searchvalue必需。规定需检索的字符串值。
    fromindex可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的最后一个字符处开始检索。

    返回值

    如果在 stringObject 中的 fromindex 位置之前存在 searchvalue,则返回的是出现的最后一个 searchvalue 的位置。

    3、toLowerCase()的定义和用法

    toLowerCase() 方法用于把字符串转换为小写。

    语法

    stringObject.toLowerCase()

    返回值

    一个新的字符串,在其中 stringObject 的所有大写字符全部被转换为了小写字符。

    4、最后进行文件格式的判断时用到了linq.js

    linq.js 详细介绍(以下内容引自只为你笑的博客)

    主要特性:

    实现所有 .NET 4.0 的方法

    complete lazy evaluation

    full IntelliSense support for VisualStudio

    two versions - linq.js and jquery.linq.js (jQuery plugin)

    support Windows Script Host

    binding for Reactive Extensions for JavaScript(RxJS) and IntelliSense Generator -> see documentation

    NuGet install support

    示例代码:

    var jsonArray = [

        { "user": { "id": 100, "screen_name": "d_linq" }, "text": "to objects" },

        { "user": { "id": 130, "screen_name": "c_bill" }, "text": "g" },

        { "user": { "id": 155, "screen_name": "b_mskk" }, "text": "kabushiki kaisha" },

        { "user": { "id": 301, "screen_name": "a_xbox" }, "text": "halo reach" }

    ]

    // ["b_mskk:kabushiki kaisha", "c_bill:g", "d_linq:to objects"]

    var queryResult = Enumerable.From(jsonArray)

        .Where(function (x) { return x.user.id < 200 })

        .OrderBy(function (x) { return x.user.screen_name })

        .Select(function (x) { return x.user.screen_name + ':' + x.text })

        .ToArray();

    // shortcut! string lambda selector

    var queryResult2 = Enumerable.From(jsonArray)

        .Where("$.user.id < 200")

        .OrderBy("$www.cnzhaotai.com/ .user.screen_name")

        .Select("$.user.screen_name + ':' + $.text")

        .ToArray();

    /

     

    用途:方便js操作查询json数据。

    下载网址:http://jslinq.codeplex.com/

    使用方法:只需要引用linq.js即可。

    查询方法:

    一、where查询

    var myList = [

      { Name: "Jim", www.wanmeiyuele.cn Age: 20 },

      { Name: "Kate", www.leyouzaixan.cn  Age: 21 },

      { Name: "Lilei", Age: 18 },

      { Name: "John", Age: 14 },

      { Name: "LinTao", www.yszx11.cn/  Age: 25 }

    ];

    var arrRes = Enumerable.From(myList).Where("x=>x.Name=='Jim'").ToArray();

    二、排序:OrderBy

    var myList = [

      { Name: "Jim",www.baohuayule.com  Age: 20 },

      { Name: "Kate", Age: 21 },

      { Name: "Lilei",www.baohuayule.cn  Age: 18 },

      { Name: "John", Age: 14 },

      { Name: "LinTao",www.006665.cn  Age: 25 }

    ];

    var arrRes = Enumerable.From(myList).OrderBy("x=>x.Age").ToArray();//降序OrderByDescending() 

    三、去重:Distinct

    var myList = [

      { Name: "Jim", Age: 20 },

      { Name: "Kate", Age: 20 },

      { Name: "Lilei", Age: 20 },

      { Name: "John", Age: 14 },

      { Name: "LinTao", Age: 25 }

    ];

    var arrRes = Enumerable.From(myList).Distinct("x=>x.Age").ToArray(); 

    四、遍历:ForEach

    var myList = [

      { Name: "Jim", Age: 20 },

      { Name: "Kate", Age: 20 },

      { Name: "Lilei", Age: 20 },

      { Name: "John", Age: 14 },

      { Name: "LinTao", Age: 25 }

       ];

       Enumerable.From(myList).ForEach(function(value, index){

            document.write("值="+value+",索引="+index);  

    }); 

    五、取唯一对象:First、FirstOrDefault、Last、LastOrDefault、Single、SingleOrDefault

    var myList = [

      { Name: "Jim", Age: 20 },

      { Name: "Kate", Age: 20 },

      { Name: "Lilei", Age: 20 },

      { Name: "John", Age: 14 },

      { Name: "LinTao", Age: 25 }

    ];

    var arrRes = Enumerable.From(myList).FirstOrDefault("x=>x.Age>18");

    六、Skip、Take

    Enumerable.Range(1,10).Skip(5)//结果[6,7,8,9,10]

    Enumerable.Range(1,10).Take(5)//结果[1,2,3,4,5]

     

    展开全文
  • 各种文件名后缀汇总
  • 最近总是需要打包不同目录下的代码,之前都是find 之后 使用tar命令来打包,总是输入长串命令,... 必须把文件后缀 以变量的形式传入才行,下边是自己的代码! #!/bin/bash CUR_DIR=`pwd` FILE_TYPE_H="h" FILE_T...
  • 在webpack.config.js中加入如下代码
  • 直接在webpack.config.js配置文件中加以下代码即可。 一般react和vue都是用ES6中的import…from…来引用文件,上面图片中的extensions指定了from后可导入的文件类型。vue中引入组件一般是vue后缀,在上面数组中加.....
  • Linux下的文件类型及文件后缀名详解

    千次阅读 2018-09-19 16:53:49
    2013年11月19日 14:29:51 jack-zhu 阅读数:5336 标签: Linux下的文件类型及文件后缀名详解 Linux的文件类型及文件后缀名 Linux文件类型 Linux的文件缀名 更多 个人分类: linux Linux下的文件类型及文件后缀名详解...
  • C#各个文件后缀含义

    千次阅读 2020-01-17 20:30:47
    代码都写在这里,主要就看这里的代码。 .Designer.cs 设计文件,自动生成 .resx 资源文件,一些资源存放在这里 .csproj C#项目文件,用VS打开这个文件就可以直接打开这个项目,自动生成,xml文件,新增文件会多...
  • python文件的后缀名是什么

    千次阅读 2021-01-29 08:37:07
    python文件的后缀名有:“.py”、“.py3”、“.pyc”、“.pyo”、“.pyd”、“.pyi”、“.pyx”、“.pyz”、“.pywz”、“.rpy”、“.pyde”、“.pyp”、“.pyt”。python文件后缀总结:(1).py:以 py 扩展名的文件...
  • 文件上传是一个在开发中很常见的需求场景,通常出于安全考虑,我们会对上传的文件进行类型校验,其中常见的有后缀名校验,mime-type校验 话不多说,直接上代码 1.首先定义允许上传的文件类型白名单 private static final ...
  • Python 相关文件常见的后缀名详解

    千次阅读 多人点赞 2021-05-02 17:20:42
    常见的 Python 文件后缀有:py、pyc 、pyo、 pyi、pyw、 pyd、 pyx 等。 本文只介绍相对常见的...实际上如果用 python + 文件 的方式运行代码,只要文件内容相同,后缀名是不重要的,也就是说下面的运行结果都是等价.
  • python 后缀

    千次阅读 2020-12-02 13:45:54
    广告关闭回望2020,你在技术之路上,有什么收获和成长么?对于未来,你有什么期待么?云+社区年度征文,各种定制好礼等你... 开头的 文件后缀名 。 codeimport osfile = hello.py # 获取前缀(文件名称)assertos.pat...
  • 文件名后缀仅用于提供给系统 shell 一个关于文件类型的「暗示」。更改文件后缀名不会改变文件的「格式」,只会影响「系统默认打开这个文件的方式」。文件是什么? 文件是存储在系统存储空间(如磁盘)中的一段数据。...
  • 【判断题】Java的源代码中定义几个类,编译结果就生成几个以“.class”后缀的字节码。 【单选题】定义主类的类头时可以使用的访问控制符是( ) 【判断题】Java支持多重继承。 【判断题】Unicode码与ASCII码相同。
  • Linux下各种后缀名文件安装

    千次阅读 2021-05-08 21:33:13
    Linux下各种后缀名文件安装刚刚开始接触Linunx,摸索了几天也有了一点点眉目,现在在Linux下终于可以上网了,方便多了。一直头疼文件安装,安装文件下载下来了却不知道怎么安装。在网上找到一篇很好的文章,很适合像...
  • Unity的AB超详细+代码注释,小白一看就懂!

    千次阅读 多人点赞 2020-10-21 21:08:29
    特别注意: 一个AB对应两个文件,其中一个只包含资源内容本身,无后缀是一个二进制压缩文件,另外一个后缀为.manifest是对这个AB的描述文件,其中描述了AB包中的资源列表以及依赖关系。 而在打包目录会额外生成...
  • EJS 什么是EJS后缀文件 EJS怎么用

    千次阅读 2019-11-08 18:03:31
    与最初的JavaScript相比较,一些不太了解你的代码的人可以更容易地通过EJS模板代码看得懂你的代码。 让我们放松一下,一起来享受下令人激动的干净简洁的感觉。 总之可以让代码更加干净整洁,让人易懂。 可以看...
  • 文件查找:find命令,文件名后缀

    千次阅读 2020-12-21 00:44:24
    二、Linux文件后缀名 Linux下的文件有后缀名(课自定义后缀名),但不代表文件的类型,不像Windows下比如file.txt是文本文档、file.exe是可执行程序。 .sh 脚本或批处理文件 .bz2 bzip2的压缩文件 .gz gzip的压缩...
  • 大家使用eclipse或者MyEclipse敲代码的时候,是不是都被这样一个问题困扰着。就是你键入一个变量名的时候,会自动提示补全,就是在你的变量名后面加上类型的名字。   增强的补全功能,这个大家应该有设置吧。这么...
  • 后缀自动机详解

    万次阅读 多人点赞 2017-03-26 11:34:54
    后缀自动机   后缀自动机(单词的有向无环图)——是一种强有力的数据结构,让你能够解决许多字符串问题。 例如,使用后缀自动机可以在某一字符串中搜索另一字符串的所有出现位置,或者计算不同子串的个数——这...
  • 但是默认的自动补齐有的时候很恶心,特别是在你定义变量的时候,自动在变量名的后面加上类型后缀。其实加后缀这个提示挺好的,但是并一定符合每个人的习惯,或者每个公司的代码规范,所以还是想办法去掉。转
  • 所有文件后缀名查询

    千次阅读 多人点赞 2018-03-01 08:46:28
    含义编辑ISO:镜像文件RAR:压缩包html:网页zip:压缩包exe:安装包pdf:pdf文档rm:视频文件avi:视频文件tmp:临时文件xls:excel工作表mdf:虚拟光驱镜像文件txt:记事本doc:word文档MID:声卡声乐文件文件类型 ...
  • 后缀表达式简介 后缀表达式,简单地说,就是一种运算符在操作数后面的表达式,后缀表达式有个很重要的特点就是可以去掉中缀表达式的括号但是又保留运算的优先级,这样便于计算机计算表达式。而我们数学上使用的是...
  • c语言源文件经过编译后生成文件的后缀是什么c语言源文件经过编译后生成文件的后缀是“.obj”。C语言源程序经过编译程序编译之后,生成一个后缀为“.obj”的文件,最后由称为“连接程序”的软件,把此“.obj”文件与...
  • 文件包含漏洞(绕过姿势)

    千次阅读 2021-04-23 08:56:53
    .apr,”MS Compound Document v1 or Lotus Approach APRfile” 文件上传绕过校验姿势 客户端绕过(抓) 服务端绕过文件类型 文件头 文件后缀名 配合文件包含漏洞绕过 配合服务器解析漏洞绕过 CMS、编辑器漏洞...
  • 这两天公司有一个需求就是有一个来自于别人的项目,只有tomcat下部署的war和一个配合程序使用的jar,额。。。。于是狼来了(问题来了。。呵呵),我需要将class文件反编译成java文件,这样一看,很简单啊,找个...
  • 移动端Kotlin 基础语法.kt 为后缀

    千次阅读 2021-03-13 15:13:30
    声明代码文件的开头一般为的声明:package com.runoob.mainimport java.util.*fun test() {}class Runoob {}kotlin源文件不需要相匹配的目录和包,源文件可以放在任何文件目录。以上例中 test() 的全名是 ...
  • 英特尔芯片的后缀The suffix lettering for Intel processors can seem like some kind of secret code at times, but what do they actually mean or stand for? Today’s SuperUser Q&A post helps clear ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 198,137
精华内容 79,254
关键字:

代码包后缀类型