Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

android 数据解析总结(各种解析)

$
0
0

从事android开发以来,解析过不少数据,都是根据所谓的协议来的。其实,这些协议都是双方约定或者一方约定的数据格式。

1,标准的gga坐标数据解析

例如:$GPGGA,033744,2446.5241,N,12100.1536,E,1,10,0.8,133.4,M,,,,*1F

看看geomap源码是怎么对坐标数据的解析的 NMEA 0183

/*
 * OpenNMEA - A Java library for parsing NMEA 0183 data sentences
 * Copyright (C)2006 Joey Gannon
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package com.opennmea;

/**
 * An object for storing and comparing geographic latitude and longitude
 * 
 * @author Joey Gannon
 * @version 1.00, 08/11/06
 * @since OpenNMEA v0.1
 */
public class Geocoordinate implements Comparable<Geocoordinate>
{
	private int latdeg,londeg,latmin,lonmin;
	private double latsec,lonsec,lattot,lontot;
	private char mode;
	private boolean valid=true;

	/**
	 * Constructs a Geocoordinate from latitude and longitude strings, with cardinal direction
	 * indicated by separate strings
	 * <p>
	 * This constructor defaults to the decimal minutes output mode.
	 * <p>
	 * If the strings passed to this constructor are malformed, the values will default to
	 * <code>{0.0, 0.0}</code>. 
	 * @param lat Latitude, in the form DDmm[.mmmm]
	 * @param ns North/South ("N" or "S")
	 * @param lon Longitude, in the form DDDmm[.mmmm]
	 * @param ew East/West ("E" or "W")
	 */
	public Geocoordinate(String lat,String ns,String lon,String ew)
	{
		this(lat.equals("")?0.0:(Integer.parseInt(lat.substring(0,2))+Double.parseDouble(lat.substring(2))/60)*(ns.equals("N")?1:-1),lon.equals("")?0.0:(Integer.parseInt(lon.substring(0,3))+Double.parseDouble(lon.substring(3))/60)*(ew.equals("E")?1:-1));
		if((lat.equals(""))||(lon.equals("")))
			valid=false;
		mode='M';
	}

	/**
	 * Constructs a Geocoordinate from floating-point latitude and longitude values, with
	 * cardinal direction indicated by numerical sign
	 * <p>
	 * This constructor defaults to the decimal degrees output mode.
	 * @param lat Latitude, in decimal degrees (south is negative)
	 * @param lon Longitude, in decimal degrees (west is negative)
	 */
	public Geocoordinate(double lat,double lon)
	{
		while(lat<-90)
			lat+=180;
		while(lat>90)
			lat-=180;
		while(lon<=-180)
			lat+=360;
		while(lon>180)
			lat-=360;
		lattot=lat;
		lontot=lon;
		latdeg=(int)lat;
		londeg=(int)lon;
		latmin=(int)(60*(lat-latdeg));
		lonmin=(int)(60*(lon-londeg));
		latsec=60*(60*(lat-latdeg)-latmin);
		lonsec=60*(60*(lon-londeg)-lonmin);
		mode='D';
	}

	/**
	 * Sets the output mode to decimal degrees
	 */
	public void toDegrees()
	{
		mode='D';
	}

	/**
	 * Sets the output mode to degrees and decimal minutes
	 */
	public void toMinutes()
	{
		mode='M';
	}

	/**
	 * Sets the output mode to degrees, minutes, and decimal seconds
	 */
	public void toSeconds()
	{
		mode='S';
	}

	/**
	 * Tells where the current mode applies the fractional part of the latitude
	 * and longitude values
	 * <p>
	 * Possible modes are degrees (<code>'D'</code>), minutes (<code>'M'</code>),
	 * or seconds (<code>'S'</code>).
	 * @return the current mode
	 */
	public char getMode()
	{
		return mode;
	}

	/**
	 * Returns the latitude, formatted according to the current mode
	 * <p>
	 * If the latitude stored by this Geocoordinate was 12.3456, then
	 * the array returned for each mode would be:<br>
	 * Degrees mode: <code>[12.3456]</code><br>
	 * Minutes mode: <code>[12.0, 20.736]</code><br>
	 * Seconds mode: <code>[12.0, 20.0, 44.16]</code>
	 * @return the latitude
	 * @see Geocoordinate#getLatitudeDegrees()
	 */
	public double[] getLatitude()
	{
		double[] array;
		if(mode=='D')
		{
			array=new double[1];
			array[0]=lattot;
		}
		else if(mode=='M')
		{
			array=new double[2];
			array[0]=(double)latdeg;
			array[1]=latmin+latsec/60;
		}
		else
		{
			array=new double[3];
			array[0]=(double)latdeg;
			array[1]=(double)latmin;
			array[2]=latsec;
		}
		return array;
	}

	/**
	 * Returns the latitude in decimal degrees
	 * <p>
	 * This is equivalent to <code>getLatitude()[0]</code> in Degrees mode.
	 * @return the latitude
	 * @see Geocoordinate#getLatitude()
	 */
	public double getLatitudeDegrees()
	{
		return lattot; 
	}

	/**
	 * Returns the longitude, formatted according to the current mode
	 * <p>
	 * If the longitude stored by this Geocoordinate was 12.3456, then
	 * the array returned for each mode would be:<br>
	 * Degrees mode: <code>[12.3456]</code><br>
	 * Minutes mode: <code>[12.0, 20.736]</code><br>
	 * Seconds mode: <code>[12.0, 20.0, 44.16]</code>
	 * @return the longitude
	 * @see Geocoordinate#getLongitudeDegrees()
	 */
	public double[] getLongitude()
	{
		double[] array;
		if(mode=='D')
		{
			array=new double[1];
			array[0]=lontot;
		}
		else if(mode=='M')
		{
			array=new double[2];
			array[0]=(double)londeg;
			array[1]=lonmin+lonsec/60;
		}
		else
		{
			array=new double[3];
			array[0]=(double)londeg;
			array[1]=(double)lonmin;
			array[2]=lonsec;
		}
		return array;
	}

	/**
	 * Returns the longitude in decimal degrees
	 * <p>
	 * This is equivalent to <code>getLongitude()[0]</code> in Degrees mode.
	 * @return the longitude
	 * @see Geocoordinate#getLongitude()
	 */
	public double getLongitudeDegrees()
	{
		return lontot;
	}
	
	/**
	 * Determines if a coordinate is valid
	 * <p>
	 * An invalid coordinate could be generated by the constructor if
	 * passed invalid strings. This method is helpful in filtering out
	 * such coordinates.
	 * @return true if the coordinate is valid, false otherwise
	 */
	public boolean isValid()
	{
		return valid;
	}

	/**
	 * Returns the distance between two Geocoordinates on Earth, in meters
	 * <p>
	 * In order to fulfill the contract of <code>Comparable</code>, this method returns
	 * a negative distance if <code>this</code> is farther south than <code>g</code>. If
	 * <code>this</code> and <code>g</code> are at the same latitude, then this method
	 * returns a negative distance if <code>this</code> is farther west than <code>g</code>.
	 * @return the distance between two Geocoordinates
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	public int compareTo(Geocoordinate g)
	{
		return (int)(Math.toDegrees(Math.acos(Math.sin(Math.toRadians(lattot))*Math.sin(Math.toRadians(g.getLatitudeDegrees()))+Math.cos(Math.toRadians(lattot))*Math.cos(Math.toRadians(g.getLatitudeDegrees()))*Math.cos(Math.toRadians(lontot-g.getLongitudeDegrees()))))*111189.577)*(lattot==g.getLatitudeDegrees()?(lontot>g.getLongitudeDegrees()?1:-1):(lattot>g.getLatitudeDegrees()?1:-1));
	}

	/**
	 * Determines if this Geocoordinate is equal to another object
	 * <p>
	 * A comparison only makes sense if the object being compared is also a Geocoordinate,
	 * so this method returns false if the parameter is not an instance of Geocoordinate.
	 * @return true if the objects are equal, false otherwise
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object o)
	{
		if(!(o instanceof Geocoordinate))
			return false;
		return ((lattot==((Geocoordinate)o).getLatitudeDegrees())&&(lontot==((Geocoordinate)o).getLongitudeDegrees()));
	}

	/**
	 * Returns a string representation of the coordinate
	 * @return a string representation of the coordinate
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		if(mode=='D')
			return (latdeg+latmin/60)+"бу"+' '+(londeg+lonmin/60)+"бу";
		else if(mode=='M')
			return latdeg+"бу"+Math.abs(latmin)+'\''+' '+londeg+"бу"+Math.abs(lonmin)+'\'';
		else
			return latdeg+"бу"+Math.abs(latmin)+'\''+Math.abs(latsec)+'\"'+' '+londeg+"бу"+Math.abs(lonmin)+'\''+Math.abs(lonsec)+'\"';
	}
}

其中经纬度都是有正负,以及南北半球,东西半球之分的

上面的代码主要就是

纬度解析:lat.equals("")?0.0:(Integer.parseInt(lat.substring(0,2))+Double.parseDouble(lat.substring(2))/60)*(ns.equals("N")?1:-1)

经度解析:lon.equals("")?0.0:(Integer.parseInt(lon.substring(0,3))+Double.parseDouble(lon.substring(3))/60)*(ew.equals("E")?1:-1)

关于其他的mnea 0183经纬度解析可参照:GPS NMEA-0183标准详解(常用的精度以及经纬度坐标)

2,数据解析

数据解析,收到的数据如下
数据分析,前面的006是没什么用的。Ed开始,后面跟着一个空格,或者是回车换行。
1.811后面也是回车换行。

006Ed 0010,0,0.000,0.0,-99998.446,-99998.102,1.811

解出来需要的数据如下:-99998.446
-99998.102
1.811

代码解析:

		private void totalStationData(int nLength, byte[] data){
			try {
				String dyString1 = new String(data, 0, nLength);
				if (dyString1.contains("006\r\n")) {
					return;
				}
					strData = strData + dyString1;
					if (strData.contains("Ed" )&& strData.substring(strData.length()-2,strData.length()).contains("\r\n")&& strData.length()>15) {
						String[] srcData = strData.split("Ed");
						if (srcData[1] != null && srcData[1].length() >=20) {
							Log.i("Show", strData);
							
							String[] srcStrings =  srcData[1].split(",");
							if (srcStrings != null && srcStrings.length >= 5) {
								if (srcStrings[4].substring(0, 1).equals("-")) {
									mdTotalStationCoord[0] = -Double.valueOf(srcStrings[4].substring(1,srcStrings[4].length()));
								}else {
									mdTotalStationCoord[0] = Double.valueOf(srcStrings[4]);
								}
								Log.i("Show",String.valueOf(mdTotalStationCoord[0]));
								
								if (srcStrings[5].substring(0, 1).equals("-")) {
									mdTotalStationCoord[1] = -Double.valueOf(srcStrings[5].substring(1,srcStrings[5].length()));
								}else {
									mdTotalStationCoord[1] = Double.valueOf(srcStrings[5]);
								}
								Log.i("Show", String.valueOf(mdTotalStationCoord[1]));
								
								if (srcStrings[6].substring(0, 1).equals("-")) {
									mdTotalStationCoord[2] = -Double.valueOf(srcStrings[6].substring(1,6));
								}else {
									mdTotalStationCoord[2] = Double.valueOf(srcStrings[6].substring(0, 5));
								}
								
								Log.i("Show", String.valueOf(mdTotalStationCoord[2]));
								strData = "";
							}	
						}
				}				
			} catch (Exception e) {
				strData = "";
			}
		}

需要注意地方是,数据接收时,是断断续续从缓冲区取出来,不是一次性的。可能间隔就是几百毫秒。
这种时候,需要特别了解每种情况接收到最后的数据时什么样。找出每条完整数据的共性。

抓住主要特点,想到各种情况。
会遇到很多很坑的数据解析
这里只提供一种参考思路。

3,物联网一般数据解析

跟硬件那边传输数据,一般都是十六进制传输的数据,所以一般先解析出来十六进制的字符串后者十六进制的字符数组

收到原始数据:

byte[] buffer = new byte[] {104, 56, 56, 104, 0, 114, 120, 85, 52, 18, 67, 35, 1, 7, 0, 0, 0, 0, 12, 19, 120, 86, 52, 18, 12,

59, 120, 52, 18, 12, 38, 120, 86, 52, 18, 11, 89, 69, 35, 0, 2, -3, 23, 0, 0, 22};

	/**
	 * byte数组转换成16进制字符数组
	 * 
	 * @param src
	 * @return
	 */
	public static String[] bytesToHexStrings(byte[] src) {
		if (src == null || src.length <= 0) {
			return null;
		}

		String[] str = new String[src.length];

		for (int i = 0; i < src.length; i++) {
			str[i] = String.format("%02X", src[i]);
		}
		
		return str;
	}


然后就是根据不同位数提取有用的数据

			//总流量
			if (strResult.length != 0) {
				if (strResult[19].equalsIgnoreCase("0C") && strResult[20].equalsIgnoreCase("13")) {
					String strWater = strResult[24] + strResult[23] + strResult[22] + strResult[21];
					double dWater = Double.valueOf(strWater);
					mtextViewShow1.setText(String.valueOf(dWater/1000) + "m³");
				}

				//流速
				if (strResult[25].equalsIgnoreCase("0C") && strResult[26].equalsIgnoreCase("3B")) {
					String strFlow = strResult[30]+strResult[29]+strResult[28]+strResult[27];
					double dFlow = Double.valueOf(strFlow);
					
					mtextViewShow2.setText(String.valueOf(dFlow/1000) + "m³/h");					
				}

				//温度
				if (strResult[37].equalsIgnoreCase("0B") && strResult[38].equalsIgnoreCase("59")) {
					String strTemp = strResult[41]+strResult[40]+strResult[39];
					double dTemp = Double.valueOf(strTemp);
					
					mtextViewShow5.setText(String.format("%.2f", dTemp/100) + "℃");
				}

				if (strResult[42].equalsIgnoreCase("02")
						&& strResult[43].equalsIgnoreCase("FD") && strResult[44].equalsIgnoreCase("17")) {
					String hexResult = StringUtil.reverseString(StringUtil.hexStringToBinary(strResult[45]));
					
					if(hexResult == null){
						return;
					}
					
					String str = hexResult.substring(5, 6);
					
					//0表示管道正常           倒数第三位
					if (str.equalsIgnoreCase("0")) {
						mtextViewShow3.setText("Normal");
					}
					//1表示空管;
					else if (str.equalsIgnoreCase("1")) {
						mtextViewShow3.setText("Empty");
					}
					
					// 最后两位
					String str1 = hexResult.substring(6, 8);
					//01 表示静止
					if (str1.equalsIgnoreCase("01") || str1.equalsIgnoreCase("11")) {
						mtextViewShow4.setText("Flowing");
					}
					//00 表示启动
					if (str1.equalsIgnoreCase("00")) {
						mtextViewShow4.setText("Start");
					}
					//10,11表示流动
					if (str1.equalsIgnoreCase("10")) {
						mtextViewShow4.setText("Stagnant");
					}
				}
			}

都是根据约定提取数据啦

最后附上一个常用进制转换工具类

package com.aufw.util;

import java.io.ByteArrayOutputStream;

public class StringUtil {
    //  十六进制的字符串转换成byte数组	
	public static byte[] HexCommandtoByte(byte[] data) {
		if (data == null) {
			return null;
		}
		int nLength = data.length; 
		
		String strTemString = new String(data, 0, nLength);
		String[] strings = strTemString.split(" ");
		nLength = strings.length;
		data = new byte[nLength];			
		for (int i = 0; i < nLength; i++) {
			if (strings[i].length() != 2) {
				data[i] = 00;
				continue;
			}
			try {
				data[i] = (byte)Integer.parseInt(strings[i], 16);
			} catch (Exception e) {
				data[i] = 00;
				continue;
			}
		}
	
		return data;
	}
	
	/**
	 * byte数组转换成16进制字符数组
	 * 
	 * @param src
	 * @return
	 */
	public static String[] bytesToHexStrings(byte[] src) {
		if (src == null || src.length <= 0) {
			return null;
		}

		String[] str = new String[src.length];

		for (int i = 0; i < src.length; i++) {
			str[i] = String.format("%02X", src[i]);
		}
		
		return str;
	}
	
    /**
     * 十六进制字符串装十进制
     * 
     * @param hex
     *            十六进制字符串
     * @return 十进制数值
     */
    public static int hexStringToAlgorism(String hex) {
    	if (hex == null) {
    		return 0;
		}
        hex = hex.toUpperCase();
        int max = hex.length();
        int result = 0;
        for (int i = max; i > 0; i--) {
            char c = hex.charAt(i - 1);
            int algorism = 0;
            if (c >= '0' && c <= '9') {
                algorism = c - '0';
            } else {
                algorism = c - 55;
            }
            result += Math.pow(16, max - i) * algorism;
        }
        return result;
    }
	
	// 十六进制的字符串转化为String
	private static String hexString = "0123456789ABCDEF";
	public static String decode(String bytes) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream(
				bytes.length() / 2);
		// 将每2位16进制整数组装成一个字节
		for (int i = 0; i < bytes.length(); i += 2)
			baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString
					.indexOf(bytes.charAt(i + 1))));
		return new String(baos.toByteArray());
	}
	
	  /**
     * 十六转二进制
     * 
     * @param hex
     *            十六进制字符串
     * @return 二进制字符串
     */
    public static String hexStringToBinary(String hex) {
       	if (hex == null) {
    		return null;
		}
        hex = hex.toUpperCase();
        String result = "";
        int max = hex.length();
        for (int i = 0; i < max; i++) {
            char c = hex.charAt(i);
            switch (c) {
            case '0':
                result += "0000";
                break;
            case '1':
                result += "0001";
                break;
            case '2':
                result += "0010";
                break;
            case '3':
                result += "0011";
                break;
            case '4':
                result += "0100";
                break;
            case '5':
                result += "0101";
                break;
            case '6':
                result += "0110";
                break;
            case '7':
                result += "0111";
                break;
            case '8':
                result += "1000";
                break;
            case '9':
                result += "1001";
                break;
            case 'A':
                result += "1010";
                break;
            case 'B':
                result += "1011";
                break;
            case 'C':
                result += "1100";
                break;
            case 'D':
                result += "1101";
                break;
            case 'E':
                result += "1110";
                break;
            case 'F':
                result += "1111";
                break;
            }
        }
        return result;
    }
    
    /** 
     * 倒置字符串 
     *  
     * @param str 
     * @return 
     */  
    public static String reverseString(String str)  
    {  
        char[] arr=str.toCharArray();  
        int middle = arr.length>>1;//EQ length/2  
        int limit = arr.length-1;  
        for (int i = 0; i < middle; i++) {  
            char tmp = arr[i];  
            arr[i]=arr[limit-i];  
            arr[limit-i]=tmp;  
        }  
        return new String(arr);  
    } 
}

关于物联网的解析数据可查看这个

android byte字节数组转换十六进制字符串(物联网开发总结)


作者:qq_16064871 发表于2016/12/1 23:16:02 原文链接
阅读:54 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>