前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Hadoop进阶之输入路径如何正则通配?

Hadoop进阶之输入路径如何正则通配?

作者头像
我是攻城师
发布2018-05-11 17:47:25
2.2K0
发布2018-05-11 17:47:25
举报
文章被收录于专栏:我是攻城师

在hadoop的编程中,如果你是手写MapReduce来处理一些数据,那么就避免不了输入输出参数路径的设定,hadoop里文件基类FileInputFormat提供了如下几种api来制定:

如上图,里面有 (1)addInputPath(),每次添加一个输入路径Path (2)addInputPaths, 将多个路径以逗号分割的字符串,作为入参,支持多个路径 (3)setInputPath ,设置一个输入路径Path,会覆盖原来的路径 (4)setInputPath , 设置多个路径,支持Hadoop文件系统重写的Path对象,这在JAVA里是接口。 代码如下:

Java代码

  1. FileInputFormat.setInputDirRecursive(job, true);//设置可以递归读取目录
  2. FileInputFormat.addInputPath(job, new Path("path1"));
  3. FileInputFormat.addInputPaths(job, "path1,path2,path3,path....");
  4. FileInputFormat.setInputPaths(job, new Path("path1"),new Path("path2"));
  5. FileInputFormat.setInputPaths(job, "path1,path2,path3,path....");
代码语言:javascript
复制
   FileInputFormat.setInputDirRecursive(job, true);//设置可以递归读取目录
			FileInputFormat.addInputPath(job, new Path("path1"));
			FileInputFormat.addInputPaths(job, "path1,path2,path3,path....");
			FileInputFormat.setInputPaths(job, new Path("path1"),new Path("path2"));
			FileInputFormat.setInputPaths(job, "path1,path2,path3,path....");

而真正用的时候,我们只需要根据业务使用上面的其中一个路径即可。 ok知道怎么,传入路径了,下面来看下,如何在HDFS上过滤出,自己想要的文件或目录,HDFS系统的路径默认是支持正则过滤的,这一点非常强大,只要我们会写正则,我们几乎可以过滤任何我们想要的路径或文件。 详细内容请查阅这个链接http://hadoop.apache.org/docs/current/api/org/apache/hadoop/fs/FileSystem.html#globStatus(org.apache.hadoop.fs.Path) 下面散仙就举个实际项目应用中的例子,这样能帮助大家更好的理解和使用它。 先看下面的一个HDFS上的存储结构图:

这是一个按日期每天生成的一个文件夹,当然这里可以有很多分维度的法,比如按照年,月,日,小时,来划分,具体情况应跟业务结合考虑。 看下,直接的根目录的下一级目录:

ok,存储结构清楚了,那么现在提几个需求 (1)只过滤出pv目录下的数据 (2)只过滤出uv目录下的数据 (3)只过滤出keyword目录下的数据 (4)只过滤出pv和uv的数据或者叫以v结尾的数据 (5)过滤2015年的数据 (6)过滤出某个时间范围内的数据比如2015-04-10到2015-04-17时间范围下的pv的数据 其实前个需求很简单都是一种需求: hadoop里的FileStatus类是支持路径通配的,对应的写法如下:

Java代码

  1. FileSystem fs = FileSystem.get(conf);
  2. //
  3. //过滤pv或uv的目录数据
  4. String basepath="/user/d1/DataFileShare/Search/*/*/{pv,uv}";
  5. //过滤v结尾的目录数据
  6. String basepath="/user/d1/DataFileShare/Search//*/*/*v";
  7. //过滤uv的数据
  8. String basepath="/user/d1/DataFileShare/Search//*/*/uv";
  9. //过滤pv的数据
  10. String basepath="/user/d1/DataFileShare/Search//*/*/pv";
  11. //过滤2015年的pv的数据
  12. String basepath="/user/d1/DataFileShare/Search/2015*/*/pv";
  13. //获取globStatus
  14. FileStatus[] status = fs.globStatus(new Path(basepath));
  15. for(FileStatus f:status){
  16. //打印全路径,
  17. System.out.println(f.getPath().toString());
  18. //打印最后一级目录名
  19. //System.out.println(f.getPath().getName());
  20. }
代码语言:javascript
复制
	        FileSystem fs = FileSystem.get(conf);
	        //
	        //过滤pv或uv的目录数据
//	        String basepath="/user/d1/DataFileShare/Search/*/*/{pv,uv}";
	        //过滤v结尾的目录数据
//	        String basepath="/user/d1/DataFileShare/Search//*/*/*v";
	        //过滤uv的数据
//	        String basepath="/user/d1/DataFileShare/Search//*/*/uv";
	        //过滤pv的数据
//	        String basepath="/user/d1/DataFileShare/Search//*/*/pv";
	        
	        //过滤2015年的pv的数据
	        String basepath="/user/d1/DataFileShare/Search/2015*/*/pv";  
	        //获取globStatus
	        FileStatus[] status = fs.globStatus(new Path(basepath));
	        for(FileStatus f:status){
	        	//打印全路径,
	        	System.out.println(f.getPath().toString());
	        	//打印最后一级目录名
	        	//System.out.println(f.getPath().getName());
	        }

最后一个复杂,直接使用正则,会比较繁琐,而且假如有一些其他的逻辑在里面会比较难控制,比如说你拿到这个日期,会从redis里面再次匹配,是否存在,然后在做某些决定。 hadoop在globStatus的方法里,提供了一个路径重载,根据PathFilter类,通过正则再次过滤出我们需要的文件即可,使用此类,我们可以以更灵活的方式,操作,过滤路径,比如说上面的那个日期范围的判断,我们就可以根据全路径中,截取出日期,再做一些判断,并且可以再次过滤低级的路径,比如是pv,uv或keyword的路径。 实例代码如下: 调用代码:

Java代码

  1. FileStatus[] status = fs.globStatus(new Path(basepath),new RegexExcludePathAndTimeFilter(rexp_date,rexp_business, "2015-04-04", "2015-04-06"));
代码语言:javascript
复制
 FileStatus[] status = fs.globStatus(new Path(basepath),new RegexExcludePathAndTimeFilter(rexp_date,rexp_business, "2015-04-04",  "2015-04-06"));

处理代码:

Java代码

  1. /**
  2. * 实现PathFilter接口使用正则过滤
  3. * 所需数据
  4. * 加强版,按时间范围,路径过滤
  5. * @author qindongliang
  6. *
  7. * **/
  8. static class RegexExcludePathAndTimeFilter implements PathFilter{
  9. //日期的正则
  10. private final String regex;
  11. //时间开始过滤
  12. private final String start;
  13. //时间结束过滤
  14. private final String end;
  15. //业务过滤
  16. private final String regex_business;
  17. public RegexExcludePathAndTimeFilter(String regex,String regex_business,String start,String end) {
  18. this.regex=regex;
  19. this.start=start;
  20. this.end=end;
  21. this.regex_business=regex_business;
  22. }
  23. @Override
  24. public boolean accept(Path path) {
  25. String data[]=path.toString().split("/");
  26. String date=data[7];
  27. String business=data[9];
  28. return Pattern.matches(regex_business, business)&&Pattern.matches(regex,date) && TimeTools.checkDate(start, end, date);
  29. }
  30. }
  31. /**日期比较的工具类**/
  32. static class TimeTools{
  33. final static String DATE_FORMAT="yyyy-MM-dd";
  34. final static SimpleDateFormat sdf=new SimpleDateFormat(DATE_FORMAT);
  35. public static boolean cnull(String checkString){
  36. if(checkString==null||checkString.equals("")){
  37. return false;
  38. }
  39. return true;
  40. }
  41. /**
  42. * @param start 开始时间
  43. * @param end 结束时间
  44. * @param path 比较的日期路径
  45. * **/
  46. public static boolean checkDate(String start,String end,String path){
  47. long startlong=0;
  48. long endlong=0;
  49. long pathlong=0;
  50. try{
  51. if(cnull(start)){
  52. startlong=sdf.parse(start).getTime();
  53. }
  54. if(cnull(end)){
  55. endlong=sdf.parse(end).getTime();
  56. }
  57. if(cnull(path)){
  58. pathlong=sdf.parse(path).getTime();
  59. }
  60. //当end日期为空时,只取start+的日期
  61. if(end==null||end.equals("")){
  62. if(pathlong>=startlong){
  63. return true;
  64. }else{
  65. return false;
  66. }
  67. }else{//当end不为空时,取日期范围直接比较
  68. //过滤在规定的日期范围之内
  69. if(pathlong>=startlong&&pathlong<=endlong){
  70. return true;
  71. }else{
  72. return false;
  73. }
  74. }
  75. }catch(Exception e){
  76. log.error("路径日期转换异常: 开始日期: "+start+" 结束日期 "+end+" 比较日期: "+path+" 异常: "+e);
  77. }
  78. return false;
  79. }
代码语言:javascript
复制
/**
	 * 实现PathFilter接口使用正则过滤
	 * 所需数据
	 * 加强版,按时间范围,路径过滤
	 * @author qindongliang
	 * 大数据交流群:(1号群) 376932160 (2号群) 415886155
	 * 
	 * **/
	static class RegexExcludePathAndTimeFilter implements PathFilter{
		 //日期的正则
		 private final  String regex;
		 //时间开始过滤
		 private final  String start;
		 //时间结束过滤
		 private final  String end;
		 //业务过滤
		 private final  String regex_business;
	 
		public RegexExcludePathAndTimeFilter(String regex,String regex_business,String start,String end) {
			this.regex=regex;
			this.start=start;
			this.end=end;
			this.regex_business=regex_business;
		}
		
		@Override
		public boolean accept(Path path) {
			String data[]=path.toString().split("/");
			String date=data[7];
			String business=data[9];
			return  Pattern.matches(regex_business, business)&&Pattern.matches(regex,date) && TimeTools.checkDate(start, end, date);
		}
		
		
		
	}
	
	/**日期比较的工具类**/
	static class TimeTools{
		
		final static String DATE_FORMAT="yyyy-MM-dd";
		
		final static SimpleDateFormat sdf=new SimpleDateFormat(DATE_FORMAT);
		
		public static boolean cnull(String checkString){
			if(checkString==null||checkString.equals("")){
				return false;
			}
			return true;
		}
		
		/**
		 * @param start 开始时间
		 * @param end 结束时间
		 * @param path 比较的日期路径
		 * **/
		public static boolean checkDate(String start,String end,String path){
			long startlong=0;
			long endlong=0;
			long pathlong=0;
			try{
			 if(cnull(start)){
				 startlong=sdf.parse(start).getTime();
			 }
			 if(cnull(end)){
				 endlong=sdf.parse(end).getTime();
			 }
			 if(cnull(path)){
				 pathlong=sdf.parse(path).getTime();
			 }
			//当end日期为空时,只取start+的日期
			if(end==null||end.equals("")){
				if(pathlong>=startlong){
					return true;
				}else{
					return false;
				}
			}else{//当end不为空时,取日期范围直接比较
				//过滤在规定的日期范围之内
				if(pathlong>=startlong&&pathlong<=endlong){
					return true;
				}else{
					return false;
				}
				
			}
			
			}catch(Exception e){
				log.error("路径日期转换异常: 开始日期:  "+start+"  结束日期 "+end+"  比较日期: "+path+"  异常: "+e);
			}
			
			return false;
		}

总结: (1)如果只是简单的路径过滤,那么直接在路径中就使用正则通配是最简单强大的。 (2)如果是比较复杂的路径过滤,建议自定义PathFilter来封装过滤代码。 (3)如果是在建设初期的就把各个文件夹目录文件的存储规划好,这样是最好不过了,比如上面的pv是一个文件夹,然后下面是各个日期,uv是一个文件夹,然后下面是各种日期,这样从业务的角度就按维度切分好,那么我们处理起来也是非常方便的,这也就是Hive里面对应的分区功能,有了分区,我们就可以按需所取,尽量避免不必要的一些额外操作。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2015-04-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 我是攻城师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档