Quantcast
Channel: CSDN博客推荐文章
Viewing all 35570 articles
Browse latest View live

[Asp.Net]登录协同工作平台安全解决方案(优化版)

$
0
0
之前的加密解决方案,随着chrome版本45之后以及edge浏览器之后,无法识别插件。需要变更代码和驱动。
解决方案不变:

这里写图片描述

HTML:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="DzPlatForm.Logon" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>厦门赛特勒客户关系管理系统</title>
    <link rel="stylesheet" type="text/css" href="resources/css/main.css" />
    <script src="Syunew6.js" type="text/javascript"></script><!--定要包含有我们的UK单元包-->
    <script  type="text/javascript" language="javascript" >
        //加载皮肤
        var setTheme = function () {
            Ext.net.DirectMethods.GetThemeUrl(cbTheme.getValue(), {
                success: function (result) {
                    Ext.net.ResourceMgr.setTheme(result);
                }
            });
        };
        //回车出发
        document.onkeydown = function (event) {
            e = event ? event : (window.event ? window.event : null);
            if (e.keyCode == 13) {
                //执行的方法  
                //alert('回车检测到了');
                //Ext.net.DirectMethods.Login();
                login();
            }
        }


    var bConnect = 0;

    function load() {
        //如果是IE10及以下浏览器,则跳过不处理,
        if (navigator.userAgent.indexOf("MSIE") > 0 && !navigator.userAgent.indexOf("opera") > -1) return;
        try {
            var s_pnp = new SoftKey6W();
            s_pnp.Socket_UK.onopen = function () {
                bConnect = 1; //代表已经连接,用于判断是否安装了客户端服务
            }

            //在使用事件插拨时,注意,一定不要关掉Sockey,否则无法监测事件插拨
            s_pnp.Socket_UK.onmessage = function got_packet(Msg) {
                var PnpData = JSON.parse(Msg.data);
                if (PnpData.type == "PnpEvent")//如果是插拨事件处理消息
                {
                    if (PnpData.IsIn) {
                        alert("UKEY已被插入,被插入的锁的路径是:" + PnpData.DevicePath);
                    }
                    else {
                        alert("UKEY已被拨出,被拨出的锁的路径是:" + PnpData.DevicePath);
                    }
                }
            }

            s_pnp.Socket_UK.onclose = function () {

            }
        }
        catch (e) {
            alert(e.name + ": " + e.message);
            return false;
        }
    }

    var digitArray = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');

    function toHex(n) {

        var result = ''
        var start = true;

        for (var i = 32; i > 0; ) {
            i -= 4;
            var digit = (n >> i) & 0xf;

            if (!start || digit != 0) {
                start = false;
                result += digitArray[digit];
            }
        }

        return (result == '' ? '0' : result);
    }

        var login = function () {
            var IsCheck = 'N';
            if (window.location.host == "117.25.XX.YYY:XXXX") 
            {
                //如果是IE10及以下浏览器,则使用AVCTIVEX控件的方式
                if (navigator.userAgent.indexOf("MSIE") > 0 && !navigator.userAgent.indexOf("opera") > -1) return Handle_IE10();

                //判断是否安装了服务程序,如果没有安装提示用户安装
                if (bConnect == 0) {
                    window.alert("未能连接服务程序,请确定服务程序是否安装。"); return false;
                }

                var DevicePath, ret, n, mylen, ID_1, ID_2, addr;
                try {

                    //由于是使用事件消息的方式与服务程序进行通讯,
                    //好处是不用安装插件,不分系统及版本,控件也不会被拦截,同时安装服务程序后,可以立即使用,不用重启浏览器
                    //不好的地方,就是但写代码会复杂一些
                    var s_simnew1 = new SoftKey6W(); //创建UK类

                    s_simnew1.Socket_UK.onopen = function () {
                        s_simnew1.ResetOrder(); //这里调用ResetOrder将计数清零,这样,消息处理处就会收到0序号的消息,通过计数及序号的方式,从而生产流程
                    }


                    //写代码时一定要注意,每调用我们的一个UKEY函数,就会生产一个计数,即增加一个序号,较好的逻辑是一个序号的消息处理中,只调用我们一个UKEY的函数
                    s_simnew1.Socket_UK.onmessage = function got_packet(Msg) {
                        var UK_Data = JSON.parse(Msg.data);
                        //alert(Msg.data);
                        if (UK_Data.type != "Process") return; //如果不是流程处理消息,则跳过
                        switch (UK_Data.order) {
                            case 0:
                                {
                                    s_simnew1.FindPort(0); //发送命令取UK的路径
                                }
                                break; //!!!!!重要提示,如果在调试中,发现代码不对,一定要注意,是不是少了break,这个少了是很常见的错误
                            case 1:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("未发现加密锁,请插入加密锁"); s_simnew1.Socket_UK.close(); return false; }
                                    DevicePath = UK_Data.return_value; //获得返回的UK的路径
                                    s_simnew1.GetID_1(DevicePath); //发送命令取ID_1
                                }
                                break;
                            case 2:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("返回ID号错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                    ID_1 = UK_Data.return_value; //获得返回的UK的ID_1
                                    s_simnew1.GetID_2(DevicePath); //发送命令取ID_2
                                }
                                break;
                            case 3:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("取得ID错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                    ID_2 = UK_Data.return_value; //获得返回的UK的ID_2

                                    frmlogin.KeyID.value = toHex(ID_1) + toHex(ID_2);

                                    s_simnew1.ContinueOrder(); //为了方便阅读,这里调用了一句继续下一行的计算的命令,因为在这个消息中没有调用我们的函数,所以要调用这个
                                }
                                break;
                            case 4:
                                {
                                    //获取设置在锁中的用户名
                                    //先从地址0读取字符串的长度,使用默认的读密码"FFFFFFFF","FFFFFFFF"
                                    addr = 0;
                                    s_simnew1.YRead(addr, 1, "ffffffff", "ffffffff", DevicePath); //发送命令取UK地址0的数据
                                }
                                break;
                            case 5:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("读数据时错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                    s_simnew1.GetBuf(0); //发送命令从数据缓冲区中数据
                                }
                                break;
                            case 6:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("调用GetBuf时错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                    mylen = UK_Data.return_value; //获得返回的数据缓冲区中数据

                                    //再从地址1读取相应的长度的字符串,,使用默认的读密码"FFFFFFFF","FFFFFFFF"
                                    addr = 1;
                                    s_simnew1.YReadString(addr, mylen, "ffffffff", "ffffffff", DevicePath); //发送命令从UK地址1中取字符串
                                }
                                break;
                            case 7:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("读取字符串时错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                   // frmlogin.txtUserName.value = UK_Data.return_value; //获得返回的UK地址1的字符串

                                    //获到设置在锁中的用户密码,
                                    //先从地址20读取字符串的长度,使用默认的读密码"FFFFFFFF","FFFFFFFF"
                                    addr = 20;
                                    s_simnew1.YRead(addr, 1, "ffffffff", "ffffffff", DevicePath); //发送命令取UK地址20的数据
                                }
                                break;
                            case 8:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("读数据时错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                    s_simnew1.GetBuf(0); //发送命令从数据缓冲区中数据
                                }
                                break;
                            case 9:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("调用GetBuf时错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                    mylen = UK_Data.return_value; //获得返回的数据缓冲区中数据

                                    //再从地址21读取相应的长度的字符串,,使用默认的读密码"FFFFFFFF","FFFFFFFF"
                                    addr = 21;
                                    s_simnew1.YReadString(addr, mylen, "ffffffff", "ffffffff", DevicePath); //发送命令从UK地址21中取字符串
                                }
                                break;
                            case 10:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("读取字符串时错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                   // frmlogin.txtPassWord.value = UK_Data.return_value; //获得返回的UK中地址21的字符串

                                    //这里返回对随机数的HASH结果
                                    s_simnew1.EncString(frmlogin.rnd.value, DevicePath); //发送命令让UK进行加密操作

                                }
                                break;
                            case 11:
                                {
                                    if (UK_Data.LastError != 0) { window.alert("进行加密运行算时错误,错误码为:" + UK_Data.LastError.toString()); s_simnew1.Socket_UK.close(); return false; }
                                    frmlogin.return_EncData.value = UK_Data.return_value; //获得返回的加密后的字符串


                                    //!!!!!注意,这里一定要主动提交,
                                    //__doPostBack('Button1', '');

                                    //所有工作处理完成后,关掉Socket
                                    s_simnew1.Socket_UK.close();
                                    IsCheck = 'Y';
                                    Ext.net.DirectMethods.Login(IsCheck);
                                }
                                break;
                        }
                    }
                    s_simnew1.Socket_UK.onclose = function () {

                    }
                    return true;
                }
                catch (e) {
                    alert(e.name + ": " + e.message);
                }


            }

            else 
            {
                Ext.net.DirectMethods.Login(IsCheck);
            }

        }

        function Handle_IE10() {
            var DevicePath, ret, n, mylen;
            try {
                //建立操作我们的锁的控件对象,用于操作我们的锁
                var s_simnew1;
                //创建控件

                s_simnew1 = new ActiveXObject("Syunew6A.s_simnew6");

                //查找是否存在锁,这里使用了FindPort函数
                DevicePath = s_simnew1.FindPort(0);
                if (s_simnew1.LastError != 0) {
                    window.alert("未发现加密锁,请插入加密锁。");

                    return false;
                }

                //'读取锁的ID
                frmlogin.KeyID.value = toHex(s_simnew1.GetID_1(DevicePath)) + toHex(s_simnew1.GetID_2(DevicePath));
                if (s_simnew1.LastError != 0) {
                    window.alert("返回ID号错误,错误码为:" + s_simnew1.LastError.toString());
                    return false;
                }

                //获取设置在锁中的用户名
                //先从地址0读取字符串的长度,使用默认的读密码"FFFFFFFF","FFFFFFFF"
                ret = s_simnew1.YRead(0, 1, "ffffffff", "ffffffff", DevicePath);
                mylen = s_simnew1.GetBuf(0);
                //再从地址1读取相应的长度的字符串,,使用默认的读密码"FFFFFFFF","FFFFFFFF"
                frmlogin.UserName.value = s_simnew1.YReadString(1, mylen, "ffffffff", "ffffffff", DevicePath);
                if (s_simnew1.LastError != 0) {
                    window.alert("读取用户名时错误,错误码为:" + s_simnew1.LastError.toString());
                    return false;
                }

                //获到设置在锁中的用户密码,
                //先从地址20读取字符串的长度,使用默认的读密码"FFFFFFFF","FFFFFFFF"
                ret = s_simnew1.YRead(20, 1, "ffffffff", "ffffffff", DevicePath);
                mylen = s_simnew1.GetBuf(0);
                //再从地址21读取相应的长度的字符串,,使用默认的读密码"FFFFFFFF","FFFFFFFF"
                frmlogin.Password.value = s_simnew1.YReadString(21, mylen, "ffffffff", "ffffffff", DevicePath);
                if (s_simnew1.LastError != 0) {
                    window.alert("读取用户密码时错误,错误码为:" + s_simnew1.LastError.toString());
                    return false;
                }

                //这里返回对随机数的HASH结果
                frmlogin.return_EncData.value = s_simnew1.EncString(frmlogin.rnd.value, DevicePath);
                if (s_simnew1.LastError != 0) {
                    window.alert("进行加密运行算时错误,错误码为:" + s_simnew1.LastError.toString());
                    return false;
                }
                //!!!!!注意,这里一定要主动提交,
                //__doPostBack('Button1', '');

            }
            catch (e) {
                alert(e.name + ": " + e.message + "。可能是没有安装相应的控件或插件");
            }
            return true;
        }    
    </script>
    <script type="text/javascript" language="javascript">
        function reloadCode() {
            var obj = document.getElementById('imgCode');
            obj.src = "VerifyCode.aspx?";
        }

    </script>
</head>
<body onload="load()">
    <form id="frmlogin" runat="server">
    <ext:ResourceManager ID="ResourceManager1" runat="server">
    </ext:ResourceManager>
    <ext:Window ID="WinLogin" runat="server" Collapsible="false" Height="270" Icon="PackageGreen"
        Title="系统登录" Width="300" Draggable="false" Resizable="false" Closable="false"
        Modal="true" Layout="FormLayout" ButtonAlign="Right" LabelAlign="Right" >
        <Items>
            <ext:Panel ID="p1" runat="server" Border="false" Height="70" BodyStyle="background-color:transparent;" >
                <Items>
                    <ext:Image ID="imgbg" runat="server" ImageUrl="../resources/images/HeaderLogo.jpg"
                        AnchorHorizontal="100%" Width="300" />
                </Items>
            </ext:Panel>

            <ext:TextField ID="txtUserName" runat="server" FieldLabel="用户名" AllowBlank="false"
                LabelWidth="120" Width="300" EmptyText="请输入账号" AnchorHorizontal="80%" >
            </ext:TextField>

            <ext:TextField ID="txtPassWord" runat="server" FieldLabel="密 码" AllowBlank="false"
                LabelWidth="120" Width="110" InputType="Password" EmptyText="请输入密码" AnchorHorizontal="80%">
            </ext:TextField>
            <ext:TextField ID="txtVerifyCode" runat="server" FieldLabel="验证码" AllowBlank="false"
                LabelWidth="120" Width="110" EmptyText="验证码" >
            </ext:TextField>
            <ext:Panel ID="Panel11" runat="server" Border="false" Height="50" BodyStyle="background-color:transparent;padding:0px 0px 6px 0px;">
                <Content>
                    <table style="width: 100%;">
                        <tr>
                            <td style="width: 100px;">
                            </td>
                            <td>
                                <img id="imgCode" width="110" height="30" src="VerifyCode.aspx?" alt="看不清?点击更换" onclick="this.src=this.src+'?'" />
                            </td>
                            <td style="font-size: 10pt;">
                                看不清?点击图片更换
                            </td>
                        </tr>
                        <tr>
                            <td colspan="3" style="font-size: 10pt; "  align="center" >
                              运行环境:建议使用Chrome,IE8+浏览器,1024*768分辨率以上
                            </td>
                        </tr>
                    </table>
                </Content>
            </ext:Panel>

        </Items>
        <Buttons>
            <ext:Button ID="btnlogin" runat="Server" Text="登陆" Icon="Tick" >
              <Listeners>
                <Click Fn="login" />
              </Listeners>
            </ext:Button>

        </Buttons>
    </ext:Window>
    <ext:TaskManager ID="TaskManager1" runat="server">
        <Tasks>
            <ext:Task TaskID="Task1" Interval="1000" AutoRun="false">
                <DirectEvents>
                    <Update OnEvent="RefreshProgress" />
                </DirectEvents>
            </ext:Task>
        </Tasks>
    </ext:TaskManager>
    <asp:HiddenField ID="KeyID" runat="server" ></asp:HiddenField>
     <asp:HiddenField ID="rnd" runat="server" ></asp:HiddenField>
     <asp:HiddenField ID="return_EncData" runat="server" ></asp:HiddenField>

    </form>
</body>
</html>

JS:


function SoftKey6W() {
    var u = document.URL;
    var url;
    if (u.substring(0, 5) == "https") {
        url = "wss://127.0.0.1:4007/xxx";
    } else {
        url = "ws://127.0.0.1:4007/xxx";
    }

    var Socket_UK;

    if (typeof MozWebSocket != "undefined") {
        Socket_UK = new MozWebSocket(url, "usbkey-protocol");
    } else {
        this.Socket_UK = new WebSocket(url, "usbkey-protocol");
    }

    this.FindPort = function (start) {
        var msg =
        {
            FunName: "FindPort",
            start: start
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.FindPort_2 = function (start, in_data, verf_data) {
        var msg =
        {
            FunName: "FindPort_2",
            start: start,
            in_data: in_data,
            verf_data: verf_data
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.FindPort_3 = function (start, in_data, verf_data) {
        var msg =
        {
            FunName: "FindPort_3",
            start: start,
            in_data: in_data,
            verf_data: verf_data
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetVersion = function (Path) {
        var msg =
        {
            FunName: "GetVersion",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetVersionEx = function (Path) {
        var msg =
        {
            FunName: "GetVersionEx",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetID_1 = function (Path) {
        var msg =
        {
            FunName: "GetID_1",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetID_2 = function (Path) {
        var msg =
        {
            FunName: "GetID_2",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };


    this.sRead = function (Path) {
        var msg =
        {
            FunName: "sRead",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.sWrite = function (InData, Path) {
        var msg =
        {
            FunName: "sWrite",
            InData: InData,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.sWrite_2 = function (InData, Path) {
        var msg =
        {
            FunName: "sWrite_2",
            InData: InData,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.sWrite_2Ex = function (InData, Path) {
        var msg =
        {
            FunName: "sWrite_2Ex",
            InData: InData,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.sWriteEx = function (InData, Path) {
        var msg =
        {
            FunName: "sWriteEx",
            InData: InData,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.sWriteEx_New = function (InData, Path) {
        var msg =
        {
            FunName: "sWriteEx_New",
            InData: InData,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.sWrite_2Ex_New = function (InData, Path) {
        var msg =
        {
            FunName: "sWrite_2Ex_New",
            InData: InData,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };


    this.SetBuf = function (InData, pos) {
        var msg =
        {
            FunName: "SetBuf",
            InData: InData,
            pos: pos
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetBuf = function (pos) {
        var msg =
        {
            FunName: "GetBuf",
            pos: pos
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.YRead = function (Address, len, HKey, LKey, Path) {
        var msg =
        {
            FunName: "YRead",
            Address: Address,
            len: len,
            HKey: HKey,
            LKey: LKey,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.YWrite = function (Address, len, HKey, LKey, Path) {
        var msg =
        {
            FunName: "YWrite",
            Address: Address,
            len: len,
            HKey: HKey,
            LKey: LKey,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.YReadString = function (Address, len, HKey, LKey, Path) {
        var msg =
        {
            FunName: "YReadString",
            Address: Address,
            len: len,
            HKey: HKey,
            LKey: LKey,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.YWriteString = function (InString, Address, HKey, LKey, Path) {
        var msg =
        {
            FunName: "YWriteString",
            InString: InString,
            Address: Address,
            HKey: HKey,
            LKey: LKey,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SetWritePassword = function (W_Hkey, W_Lkey, new_Hkey, new_Lkey, Path) {
        var msg =
        {
            FunName: "SetWritePassword",
            W_Hkey: W_Hkey,
            W_Lkey: W_Lkey,
            new_Hkey: new_Hkey,
            new_Lkey: new_Lkey,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SetReadPassword = function (W_Hkey, W_Lkey, new_Hkey, new_Lkey, Path) {
        var msg =
        {
            FunName: "SetReadPassword",
            W_Hkey: W_Hkey,
            W_Lkey: W_Lkey,
            new_Hkey: new_Hkey,
            new_Lkey: new_Lkey,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };


    this.DecString = function (InString, Key) {
        var msg =
        {
            FunName: "DecString",
            InString: InString,
            Key: Key
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.EncString = function (InString, Path) {
        var msg =
        {
            FunName: "EncString",
            InString: InString,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.EncString_New = function (InString, Path) {
        var msg =
        {
            FunName: "EncString_New",
            InString: InString,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.Cal = function (Path) {
        var msg =
        {
            FunName: "Cal",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.Cal_New = function (Path) {
        var msg =
        {
            FunName: "Cal_New",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SetCal_2 = function (Key, Path) {
        var msg =
        {
            FunName: "SetCal_2",
            Key: Key,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SetCal_New = function (Key, Path) {
        var msg =
        {
            FunName: "SetCal_New",
            Key: Key,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SetEncBuf = function (InData, pos) {
        var msg =
        {
            FunName: "SetEncBuf",
            InData: InData,
            pos: pos
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetEncBuf = function (pos) {
        var msg =
        {
            FunName: "GetEncBuf",
            pos: pos
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };


    this.ReSet = function (Path) {
        var msg =
        {
            FunName: "ReSet",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.MacAddr = function () {
        var msg =
        {
            FunName: "MacAddr"
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };


    this.GetChipID = function (Path) {
        var msg =
        {
            FunName: "GetChipID",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.StarGenKeyPair = function (Path) {
        var msg =
        {
            FunName: "StarGenKeyPair",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GenPubKeyY = function () {
        var msg =
        {
            FunName: "GenPubKeyY"
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GenPubKeyX = function () {
        var msg =
        {
            FunName: "GenPubKeyX"
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GenPriKey = function () {
        var msg =
        {
            FunName: "GenPriKey"
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetPubKeyY = function (Path) {
        var msg =
        {
            FunName: "GetPubKeyY",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetPubKeyX = function (Path) {
        var msg =
        {
            FunName: "GetPubKeyX",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.GetSm2UserName = function (Path) {
        var msg =
        {
            FunName: "GetSm2UserName",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.Set_SM2_KeyPair = function (PriKey, PubKeyX, PubKeyY, sm2UserName, Path) {
        var msg =
        {
            FunName: "Set_SM2_KeyPair",
            PriKey: PriKey,
            PubKeyX: PubKeyX,
            PubKeyY: PubKeyY,
            sm2UserName: sm2UserName,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.YtSign = function (SignMsg, Pin, Path) {
        var msg =
        {
            FunName: "YtSign",
            SignMsg: SignMsg,
            Pin: Pin,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.YtSign_2 = function (SignMsg, Pin, Path) {
        var msg =
        {
            FunName: "YtSign_2",
            SignMsg: SignMsg,
            Pin: Pin,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.YtVerfiy = function (id, SignMsg, PubKeyX, PubKeyY, VerfiySign, Path) {
        var msg =
        {
            FunName: "YtVerfiy",
            id: id,
            SignMsg: SignMsg,
            PubKeyX: PubKeyX,
            PubKeyY: PubKeyY,
            VerfiySign: VerfiySign,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SM2_DecString = function (InString, Pin, Path) {
        var msg =
        {
            FunName: "SM2_DecString",
            InString: InString,
            Pin: Pin,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SM2_EncString = function (InString, Path) {
        var msg =
        {
            FunName: "SM2_EncString",
            InString: InString,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.YtSetPin = function (OldPin, NewPin, Path) {
        var msg =
        {
            FunName: "YtSetPin",
            OldPin: OldPin,
            NewPin: NewPin,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.FindU = function (start) {
        var msg =
        {
            FunName: "FindU",
            start: start
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.FindU_2 = function (start, in_data, verf_data) {
        var msg =
        {
            FunName: "FindU_2",
            start: start,
            in_data: in_data,
            verf_data: verf_data
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.FindU_3 = function (start, in_data, verf_data) {
        var msg =
        {
            FunName: "FindU_3",
            start: start,
            in_data: in_data,
            verf_data: verf_data
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.IsUReadOnly = function (Path) {
        var msg =
        {
            FunName: "IsUReadOnly",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SetUReadOnly = function (Path) {
        var msg =
        {
            FunName: "SetUReadOnly",
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.SetHidOnly = function (IsHidOnly, Path) {
        var msg =
        {
            FunName: "SetHidOnly",
            IsHidOnly: IsHidOnly,
            Path: Path
        };
        this.Socket_UK.send(JSON.stringify(msg));
    };

    this.ResetOrder = function () {
        var msg =
        {
            FunName: "ResetOrder"
        };
        this.Socket_UK.send(JSON.stringify(msg));
    }

    this.ContinueOrder = function () {
        var msg =
        {
            FunName: "ContinueOrder"
        };
        this.Socket_UK.send(JSON.stringify(msg));
    }

} 

C#

String strData, m_StrEnc, Key, Flag;

                    Flag = IsCheck;
                    //Key:即增强算法密钥,这个要与设置在加密锁中的密钥一致
                    //增强算法密钥可以是每一把都不相同,也可以是都相同,如果是不相同的可以根据用户名在从数据库中获取对应的增强算法密钥,可以根据安全性及自身具体情况而定,这里使用了一个固定的值
                   // Key = "1234567890ABCDEF1234567890ABCDEF";
                    Key = idf.UkeyCode;
                    //strData:要进行加密的数据
                    strData = rnd.Value.Trim();//Session["rnd"].ToString();
                    //'在服务器端对数据进行加密运算
                    m_StrEnc = Global.m_softkey.StrEnc(strData, Key);
                    //比较客户端加密锁返回的加密结果与服务端的加密结果是否相符,如果相符就认为是合法用户,由于使用了随机数,从而实现了一次一密的高安全性,可以用于高安全性的身份验证
                    //这里在服务器端对随机数进行同样的加密运算


                    if (Flag == "N")//内网不需要比对
                    {
                        Response.Redirect("Index.aspx");
                    }
                    else
                    {
                        if (m_StrEnc == return_EncData.Value)
                        {

                            Response.Redirect("Index.aspx");
                        }
                        else
                        {
                            Notification.Show(new NotificationConfig
                            {
                                Title = "提醒",
                                Icon = Icon.Information,
                                Html = "<font style='color:red;'>此UKEY与输入的用户不匹配!</font>"
                            });

                        }

                    }

这里写图片描述

作者:david_520042 发表于2016/7/22 21:58:16 原文链接
阅读:59 评论:0 查看评论

[LeetCode刷题笔记]Math数学类型题目(二)字符串型数字计算

$
0
0

原创文章

转载请注册来源http://blog.csdn.net/tostq

这类题一般都选择将数字(一般很长)转换成了字符串、数组或链表的形式
比如"12345", 1->2->3->4之后
然后以这类形式,来进行相关的数学运算。多数情况下,这类数字都比较大,在转换时非常容易出数据位溢出的情况,另外可能会有空间和时间的限制,比如重写字符串数字加法,但不允许先将字符串数字转换成整型计算后,再重新转换回来。

一、字符串型的数字运算
(1)以链表形式存在数字位
2. Add Two Numbers

数字以相反的顺序存储在链表中,也就是说低位在前,高位在后,比如
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
这个问题是比较简单,主要难点在于对于进位的处理(增加结点)。

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode dummy(0),*ptr=&dummy;
        int carry=0;
        while(l1||l2){
            int sum=carry;
            if(l1){
                sum=sum+l1->val;
                l1=l1->next;
            }
            if(l2){
                sum=sum+l2->val;
                l2=l2->next;                
            }
            ptr->next=new ListNode(sum%10);
            carry=sum/10;
            ptr=ptr->next;
        }
        if(carry)ptr->next=new ListNode(carry);
        return dummy.next;
    }
};
如果要加大难度的话,可以让数字的存储是高位在前,低位在后。

(2)以字符串形式存在数字位
67. Add Binary

同上题一样,这也是一个加法问题,不过这是一个以字符串形式的存储数字位数的题

char* addBinary(char* a, char* b) {
    int lena=strlen(a),lenb=strlen(b);
    int lenc=lena>lenb?lena:lenb;
    lenc++;
    char* c=(char*)malloc(sizeof(char)*(lenc+1));
    c[lenc]='\0';
    
    int carry=0;
    while(lena||lenb){
        if(lena)carry+=a[--lena]-'0';
        if(lenb)carry+=b[--lenb]-'0';
        c[--lenc]=(carry&1)+'0';
        carry=carry>>1;
    }
    c[0]=carry+'0';
    return c+(carry^1);
}
(3)以数组形式的存储数字位数
66. Plus One

也是加法,数字位是以数组形式或vector形式存储的。
class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        reverse(digits.begin(),digits.end());
        int i;
        for(i=0;i<digits.size();i++){
            if(digits[i]+1<10){
                digits[i]=digits[i]+1;
                break;
            }
            digits[i]=0;
        }
        if(i==digits.size())digits.push_back(1);
        reverse(digits.begin(),digits.end());
        return digits;
    }
};
可以看到对于高位在前,低位在后的存储形式,如果是C++的话,我们可以调用reverse来将数组反转,或者直接通过数组或字符串长度来倒转查取。
(4)两字符串数组相乘
43. Multiply Strings

这道题的难点
1、不能将字符串转换成整型。
2、如何处理乘法进位和加法进位
class Solution {
public:
    string multiply(string num1, string num2) {
        if(num1[0]=='0'||num2[0]=='0')return "0";
        int l1=num1.size();
        int l2=num2.size();
        string res(l1+l2,'0');
        
        for(int i=l1-1;i>=0;i--){
            int c=0;
            int r=i+l2;            
            for(int j=l2-1;j>=0;j--,r--){
                int m=(num1[i]-'0')*(num2[j]-'0')+(int)(res[r]-'0')+c;
                int st=m%10;
                c=m/10;
                //printf("|m=%d,c=%d,st=%d|",m,c,st);
                res[r]=(char)(st+'0');
                if(j==0&&c!=0)res[(r-1)]=res[(r-1)]+c;
            }
        }
        while(res[0]=='0')res.erase(0,1);
        return res;
    }
};
(5)将字符串直接转换成数字
8. String to Integer (atoi)

这道题虽然标识为简单,而实际的正确提交率只有12%,比许多困难的题目都要低
主要原因:
1、String转换成数字可能出现溢出的情况
2、对于错误表示的数字的处理
class Solution {
public:
    int myAtoi(string str) {
        while(str[0]==' ')str.erase(0,1);
        while(str[str.size()-1]==' ')str.erase(str.size()-1,1);
        int sign=1;
        if(str[0]=='-'){sign=-1;str.erase(0,1);}
        else if(str[0]=='+'){sign=1;str.erase(0,1);}
        int s=str.size();
        if(s==0)return 0;
        int res=0;
        int i=0;
        while(str[i]>='0'&&str[i]<='9'){
            if (res >  INT_MAX / 10 || (res == INT_MAX / 10 && str[i] - '0' > 7)){
                if (sign == 1) return INT_MAX;
                else return INT_MIN;
            } // overflow
            
            res=res*10+(int)(str[i]-'0');
            i++;
        }
        return sign*res;
    }
};

二、数字转换成字符串
166. Fraction to Recurring Decimal
这个题是一个计算除法结果的题目,除法的结果一般包括了小数部分,甚至无限循环,所以这里我们需要用字符串来表示输出结果。比如
输入:numerator = 1, denominator = 2, 输出:"0.5".
输入:numerator = 2, denominator = 1, 输出:"2".
输入:numerator = 2, denominator = 3, 输出:"0.(6)".
具体的解法如下:
#define ABS(x) (x>0?x:-x)
class Solution {
public:
    string inttostring(int64_t num){
        if(num==0)return "0";
        string s;
        while(num>0){
            s=(char)(num%10+'0')+s;
            num=num/10;
        }
        return s;
    }
    string fractionToDecimal(int64_t numerator, int64_t denominator) {
        string res;
        string decs;
        if((numerator>0&&denominator<0)||(numerator<0&&denominator>0))res="-";
        numerator=ABS(numerator);
        denominator=ABS(denominator);
        
        int64_t integarpart=numerator/denominator;
        int64_t restartnum=numerator%denominator;
        res=res+inttostring(integarpart);
        if(restartnum==0)return res;
        
        unordered_map<int, int> rem;
        int64_t rempart=restartnum;
        int i=0;
        rem.insert(make_pair(restartnum,i));
        while(1){
            int64_t newnum=rempart*10;
            int64_t decnum=newnum/denominator;
            decs=decs+inttostring(decnum);
            rempart=newnum%denominator;
            if(rempart==0)return res+"."+decs;
            if(rem.find(rempart)!=rem.end())return res+"."+decs.insert(rem[rempart],"(")+")";
            i++;
            rem.insert(make_pair(rempart,i));
        }
    }
};
这里定义输入与输出为int64_t跟long类型是一个意思,就是为了防止在绝对值后,数据溢出
这个算法难道在于如何判断存在循环和循环位置(循环开始位置不一定在小数点的后一位,比如1/6)。其他包括整数部分和不循环小数都是比较好解决的。
循环的判断:如果存在循环,那么当前除完后剩下的余数,将会在未来再次出现,而该余数首次出现的位置应该是循环出现的位置。1/6=0.1(6)
所以我们可以通过一个Hash表来存储余数和其位置,如下定义
unordered_map<int, int> rem;
rem.find(rempart)!=rem.end()来确定某值是否在表内
rem.insert(make_pair(rempart,i)); 插入新值

三、将数字表示其它符号
这类题非常多,主要的难点要求做题人能充分了解新符号表示的特点,
不过一旦熟悉新符号表示的特点,这类题目就不难了。
(1)数字与罗马数字的转换
罗马数字主要有{'I','V','X','L','C','D','M'},分别对应于{1,5,10,50,100,500,1000}
其将十分成了二等份,如果某数是在单位上增加了1,2,3(比如8在5的基础上增加了3,比如60(LX)在50(L)的基础上增加了10(X)),则往右添加低字,如XII=10+2,VII=5+7,XXX=30
如果是4,则往左添加低字,如IX=10-1=9
可以看出,如果较低字(比如I就比V低)在高字的前面,就说明该字要被减,否则一直都是加。
具体的解法如下:
13. Roman to Integer
class Solution {
    const char romanNum[7]={'I','V','X','L','C','D','M'};
    const int num[7]={1,5,10,50,100,500,1000};
public:
    int romanToInt(string s) {
        if(s.size()==0)return 0;
        int res=0;
        int flag=0; //wei
        int pre=0;
        for(int i=s.size()-1;i>=0;i--){
            int j=0;
            for(j=0;s[i]!=romanNum[j];j++);
            int cur=num[j];
            if(cur>=pre){
                res=res+cur;
            }else{
                res=res-cur;
            }
            pre=cur;
        }
        return res;
    }
};
这里还有一个小技巧,就是如何查询,通过for语句比switch比较要方便很多。
12. Integer to Roman
将数字转换成罗马数字就有些难度了,主要是因为罗马数字并不是十进位,而是五进位
而十进位的处理方式与五进位的处理方式是不同。
所以这里直接对其进行了分别考虑。
class Solution {
    const char romanNum[7]={'I','V','X','L','C','D','M'};
    const int nums[7]={1,5,10,50,100,500,1000};
public:
    string intToRoman(int num) {
        
        string s;
        int j=6;
        while(j>=0&&num!=0){
            for(;j>=0&&num/nums[j]==0;j--); // find the max
            if(j%2==0){
                int g=num/nums[j];
                if(g==4)s=s+romanNum[j]+romanNum[j+1];
                else
                    for(int i=0;i<g;i++)s=s+romanNum[j];
                num=num%nums[j];}
            else{
                int g=(num-nums[j])/nums[j-1];
                if(g==4)s=s+romanNum[j-1]+romanNum[j+1];
                else{
                    s=s+romanNum[j];
                    for(int i=0;i<g;i++)s=s+romanNum[j-1];
                }
                num=num%nums[j-1];
            }
        }
        return s;
    }
};
(2)数字与英文字母间的转换
数字与英文之间的转换并不困难,主要的难点:
1、进位的转换,由10进制变成了26进制
2、对于0的处理,比如下面这两道数字都是从1开始,而不是从0开始的。
题目的形式如下:
    A -> 1
    B -> 2
    C -> 3
    ...
    Z -> 26
    AA -> 27
    AB -> 28
171. Excel Sheet Column Number
int titleToNumber(char* s) {
    int res=0;
    char *t;
    t=s;
    while(*t!='\0'){
        res=res*26+(int)(*t)-(int)('A')+1;
        t++;
    }
    return res;
}

168. Excel Sheet Column Title
class Solution {
public:
    string convertToTitle(int n) {
        string s;
        while(n>0){
            s=(char)((n-1)%26+'A')+s;
            n=(n-1)/26;
        }
        return s;
    }
};

(3)数字与英文表示间的转换
273. Integer to English Words
要求:
123 -> "One Hundred Twenty Three"
12345 -> "Twelve Thousand Three Hundred Forty Five"
1234567 -> "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
我们可以看出这是以一千为进位的,所以只要把数字划成一个个的三位数,转换就不困难的,不过对于个人来说,这道题还是花了不少的时间,主要问题就是对于空格的处理。因为字符串输入结果是要完美对应的,只要前后空格不能相互对应,就会输出错误。而我的绝大多数的时间都是花在了折腾这些空格上=_=||。
下面是具体的解法。
class Solution {
private:
    const string numbers[20]={"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine","Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
    const string weibers[4]={"Hundred","Thousand","Million","Billion"};
    const string tybers[10]={"","", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
public:
    string convertHundred(int num){ // num<1000
        string res;
        int h=num/100;
        res=(h>0?(numbers[h]+" "+"Hundred"):"");
        int t=num%100;
        if(t==0) return res;
        if(t<20) return res==""?numbers[t]:res+" "+numbers[t];
        int s=t%10;
        t=t/10;
        res=(res==""?tybers[t]:res+" "+tybers[t]);
        return s==0?res:(res==""?numbers[s]:res+" "+numbers[s]);
    }
    
    string numberToWords(int num) 
    {
        if(num==0)return "Zero";
        string res = convertHundred(num%1000);
        num=num/1000;
        if(num%1000)res=convertHundred(num%1000)+" Thousand"+((res == "") ? "" : " ")+res;
        num=num/1000;
        if(num%1000)res=convertHundred(num%1000)+" Million"+((res == "") ? "" : " ")+res;
        num=num/1000;
        if(num%1000)res=convertHundred(num%1000)+" Billion"+((res == "") ? "" : " ")+res;
        return res;
    }
};









作者:tostq 发表于2016/7/22 22:00:51 原文链接
阅读:42 评论:0 查看评论

Android混淆心得

$
0
0

最近在做Android应用的混淆,踩了一些坑,这里记录分享下个人的心得。

混淆介绍

首先先简单说一下什么是混淆和混淆的作用,其实这个搜索下可以找到一堆官方的说法等等,这里简单口语叙述一下,混淆就是把代码替换成a、b、c基本字母组成的代码,比如一个方法名为:function(),混淆后可能会被替换成a()。

混淆的好处:

  • 代码混淆后阅读性降低,反编译后破译程序难度提高
  • 混淆后字节数减少,减少了应用了体积

前者只能说有一点作用,后者则需要看代码的数量
当然不能忽视混淆的缺点:

  • 混淆后,测试不充分可能导致某些功能不能使用

开启混淆

混淆在Android Studio的项目中默认是关闭的,其中控制开关和规则配置文件分别由项目moudle中的build.gradleproguard-rules.pro控制,如下图所示:

其中build.gradle中

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

其中

minifyEnabled false

表示不开启混淆,可以改为

minifyEnabled true

开启混淆,开启混淆后可以添加一句:

shrinkResources true

表示去掉没有引用的资源,可以减少应用的体积,但这个只有在混淆开启后才有效。

混淆规则文件

可以从上述的代码看出,Android自带一个混淆规则文件:

proguard-android.txt

这个文件在SDK目录下,里面有一些默认自带的规则,而我们今天需要配置的自定义配置的文件,即上面所述的

proguard-rules.pro

混淆规则基本语法

混淆文件采用白名单法,意思是不在白名单里面的都要混淆。
混淆规则的基本符号:

# 代表行注释符
- 表示一条规则的开始

一般规则用连起来单词表示,主要有:

keep 保留,例如keepattributes:表示保留属性
dont 不要,例如dontwarn:表示不要提示警告
ignore 忽略,例如ignorewarning:表示忽略警告

混淆配置文件不检查规则是否重复,如果两条规则冲突,则采用白名单的,比如设置了开启优化和不优化两个选项后,无论顺序,最终都会执行不优化的操作。

优化控制

这个是用于控制混淆是否开启优化代码,例如一些if/else语句可以被简化等这些操作:

# 不优化
-dontoptimize

# 代码循环优化次数,0-7,默认为5
-optimizationpasses 5

值得注意的是默认混淆配置文件开启了-dontoptimize

优化进阶

开启优化后可以设置下面的规则,assumenosideeffects表示指定的代码无效,可以优化,最终效果表现为不执行。

# 混淆时所采用的优化规则
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

# 关闭log
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}

基本混淆规则

下面这些一般混淆规则都要加入,其中前两个在默认文件中已经配置:

# 包名不使用大小写混合 aA Aa
-dontusemixedcaseclassnames

# 不混淆第三方引用的库
-dontskipnonpubliclibraryclasses

# 不做预校验
-dontpreverify

# 忽略警告
-ignorewarning

输出混淆记录

混淆后由于阅读困难性提高,所以为了方便自己查阅,可以输出mapping对应文件,可以利用AndroidSDK\tools\proguard\bin中的proguardgui.bat打开混淆工具,利用retrace结合mapping和stacktrace调试遇到的错误

# 混淆后生产映射文件 map 类名->转化后类名的映射
# 存放在app\build\outputs\mapping\release中
-verbose

# 混淆前后的映射
-printmapping mapping.txt

# apk 包内所有 class 的内部结构
-dump class_files.txt

# 未混淆的类和成员
-printseeds seeds.txt

# 列出从 apk 中删除的代码
-printusage unused.txt

保留源代码行号

即使使用retrace工具,还是很难定位到错误的时候,可以暂时先保留行号,观察错误修改后再关闭掉

# 抛出异常时保留代码行号
# 这个最后release的时候关闭掉
-keepattributes SourceFile,LineNumberTable

基本组件白名单

Android中的基本组件不能混淆,为了方便,下面提供了兼容性比较高的规则:

-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference

Support包规则

# 如果有引用v4包可以添加下面这行
-keep public class * extends android.support.v4.app.Fragment

# 如果引用了v4或者v7包
-dontwarn android.support.**

不混淆本地方法

本地方法不能混淆,这个规则在默认配置文件中有:

# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

WebView混淆规则

使用了WebView的JS功能则开启下面规则,这个规则在自定义规则文件中已经用注释说明了:

# WebView使用javascript功能则需要开启
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
    public *;
}

注解、泛型和反射混淆

下面是混淆规则:

# 保护注解
-keepattributes *Annotation*
-keep class * extends java.lang.annotation.Annotation {*;}

# 泛型与反射
-keepattributes Signature
-keepattributes EnclosingMethod

有些注解可能不能被混淆,需要手动混淆一下

内部类混淆

# 不混淆内部类
-keepattributes InnerClasses

第三方混淆参考规则

Gson

# gson
-dontwarn com.google.**
-keep class com.google.gson.** {*;}

otto

# otto混淆规则
-keepattributes *Annotation*
-keepclassmembers class ** {
    @com.squareup.otto.Subscribe public *;
    @com.squareup.otto.Produce public *;
}

universal-image-loader

-dontwarn com.nostra13.universalimageloader.**
-keep class com.nostra13.universalimageloader.** {*;}

友盟统计

# 友盟统计
-keepclassmembers class * {
    public <init> (org.json.JSONObject);
}

# 友盟统计5.0.0以上SDK需要
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 友盟统计R.java删除问题
-keep public class com.gdhbgh.activity.R$*{
    public static final int *;
}

OkHttp

# OkHttp
-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.** {*;}
-keep interface com.squareup.okhttp.** {*;}
-dontwarn okio.**

nineoldandroids

-dontwarn com.nineoldandroids.*
-keep class com.nineoldandroids.** {*;}

支付宝

# 支付宝
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{
    public *;
}
-keep class com.alipay.sdk.app.AuthTask{
    public *;
}

Socket.io

# socket.io
-keep class socket.io-client.
-keepclasseswithmembers,allowshrinking class socket.io-client.* {*;}
-keep class io.socket.
-keepclasseswithmembers,allowshrinking class io.socket.* {*;}

JPUSH

# jpush
-dontwarn cn.jpush.**
-keep class cn.jpush.** {*;}

# protobuf(jpush依赖)
-dontwarn com.google.**
-keep class com.google.protobuf.** {*;}

友盟分享

这个只有部分热门的SDK,具体可以参考分享文档:

# 友盟分享
-dontwarn com.umeng.**
-dontwarn com.tencent.weibo.sdk.**

-keep public interface com.tencent.**
-keep public interface com.umeng.socialize.**
-keep public interface com.umeng.socialize.sensor.**
-keep public interface com.umeng.scrshot.**

-keep public class com.umeng.socialize.* {*;}

-keep class com.umeng.scrshot.**
-keep public class com.tencent.** {*;}
-keep class com.umeng.socialize.sensor.**
-keep class com.umeng.socialize.handler.**
-keep class com.umeng.socialize.handler.*
-keep class com.tencent.mm.sdk.modelmsg.WXMediaMessage {*;}
-keep class com.tencent.mm.sdk.modelmsg.** implements com.tencent.mm.sdk.modelmsg.WXMediaMessage$IMediaObject {*;}

-keep class im.yixin.sdk.api.YXMessage {*;}
-keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;}

-keep class com.tencent.** {*;}
-dontwarn com.tencent.**
-keep public class com.umeng.soexample.R$*{
    public static final int *;
}
-keep class com.tencent.open.TDialog$*
-keep class com.tencent.open.TDialog$* {*;}
-keep class com.tencent.open.PKDialog
-keep class com.tencent.open.PKDialog {*;}
-keep class com.tencent.open.PKDialog$*
-keep class com.tencent.open.PKDialog$* {*;}

-keep class com.sina.** {*;}
-dontwarn com.sina.**
-keep class  com.alipay.share.sdk.** {*;}

个人遇到的一些坑

网络层混淆

混淆要注意,一般网络层都不进行混淆,可以经过划分包后直接不混淆网络层的包:

-keep class com.xxx.xxx.http.** {*;}

数据模型混淆

所有bean都不要混淆,可以使用下面的:

-keep class * implements java.io.Serializable {*;}
-keepclassmembers class * implements java.io.Serializable {*;}

但是有时候上述代码可能导致应用卡住,没用任何错误提示,所以我建议采用分包模式,把所有bean放在一个包中,直接对该包加白名单:

-keep class com.xxx.xxx.domain.** {*;}

XML映射问题

如果你遇到一些控件无法Inflate,报NullPointException,比如ListView,NavigationView等等,这个问题花了我几个小时自己研究出了规则:

-keep class org.xmlpull.v1.** {*;}

混淆规则编写方法

如果混淆后报错,通过retrace后找到错误的问题后可以直接编写规则来去掉混淆,但是如果报的错误莫名其妙,而且报错的类没有混淆,那么你可以采用极端的方法:

加入下面规则:

-keep class *.** {*;}

这条规则表示不混淆所有类及其中所有代码,加了这条规则之后,
还不能运行表示是其他问题,例如注解,内部类等等,
可以运行后,可以通过反编译,寻找所有包名,记录下来,把上述规则改为:

-keep class android.** {*;}
-keep class com.** {*;}
-keep class io.** {*;}
-keep class org.** {*;}
...

一个个去掉检查是否有报错,例如查到

-keep class com.** {*;}

加了就不报错,则可以继续一级级往下检查。
但要注意,有时候可能是几个包混合问题。

声明

原创文章,欢迎转载,请保留出处。
有任何错误、疑问或者建议,欢迎指出。
我的邮箱:Maxwell_nc@163.com
作者:maxwell_nc 发表于2016/7/22 22:07:21 原文链接
阅读:37 评论:0 查看评论

SpringMVC工作原理解读--DispatcherServlet类源码解读(初级)

$
0
0

我这里先通读一下DispatcherServlet的代码,首先知道各个部分是什么用处,后面再次进行串联的讲解。一开始我可能也讲的不是很明白,但是时间久了我觉得自然而然的就明白了。读者可以发布自己的意见共同交流。

DispatcherServlet类是怎么处理请求的

先来看DispatcherServlet中的代码,静态代码块

static {
		// Load default strategy implementations from properties file.
		// This is currently strictly internal and not meant to be customized
		// by application developers.
		try {
			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
		}
	}
这段代码主要完成了加载配置文件,以及设置默认参数的作用。


/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

	/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}

在这里初始化了一些数据,包括定位解析器、主题解析器、处理器映射器、处理器适配器、异常解析器、视图解析器等等,然后接下来的部分源码就是该方法中的这些方法依次的实现。注意:这个方法我没找到在哪个地方进行调用,可能不是在本类中进行的调用。


举例子看下:这个是初始化处理器映射器的方法,获取所有、指定的映射器如果这些中都没有采用默认的处理器映射器。

/**
	 * Initialize the HandlerMappings used by this class.
	 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
	 * we default to BeanNameUrlHandlerMapping.
	 */
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		else {
			try {
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
			}
		}
	}

doService方法,默认调用doService,他的父类


/**
	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
	 * for the actual dispatching.
	 */
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
		}

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
		if (inputFlashMap != null) {
			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
		}
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

		try {
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

为request设置了许多属性值,但是提前保存了一份request中的所有属性的快照并且在最后finally中进行恢复。然后调用doDispatch方法。


/**
	 * Process the actual dispatching to the handler.
	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
	 * to find the first that supports the handler class.
	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
	 * themselves to decide which methods are acceptable.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception in case of any kind of processing failure
	 */
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

这里我的理解是主要的解析以及反馈的任务都在这里完成,DispatcherServlet好像有先找处理器然后没有的话直接返回结果的动作,然后获取处理器适配器,处理器适配器去调用方法handle(),处理请求,返回ModelAndView对象,通过processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);这个方法是来完成视图的解析和渲染。


/**
	 * Handle the result of handler selection and handler invocation, which is
	 * either a ModelAndView or an Exception to be resolved to a ModelAndView.
	 */
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

这里大部分的代码在处理异常注意中间有个比较大的if else语句,这个语句是将处理推给了render方法去完成。


/**
	 * Render the given ModelAndView.
	 * <p>This is the last stage in handling a request. It may involve resolving the view by name.
	 * @param mv the ModelAndView to render
	 * @param request current HTTP servlet request
	 * @param response current HTTP servlet response
	 * @throws ServletException if view is missing or cannot be resolved
	 * @throws Exception if there's a problem rendering the view
	 */
	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale = this.localeResolver.resolveLocale(request);
		response.setLocale(locale);

		View view;
		if (mv.isReference()) {
			// We need to resolve the view name.
			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		}
		else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}

		// Delegate to the View object for rendering.
		if (logger.isDebugEnabled()) {
			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
		}
		try {
			view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
						getServletName() + "'", ex);
			}
			throw ex;
		}
	}
在这里面调用了一些方法将传递过来ModelAndView中的属性进行设置等等,完成整个的视图。调用了
view.render(mv.getModelInternal(), request, response);
我猜测的是这个方法完成视图显示给浏览器。


作者:wangyang1354 发表于2016/7/22 22:18:19 原文链接
阅读:84 评论:0 查看评论

javaEE:day7-上传文件(Apache包)、目录打散、文件上传进度条、纯前台进度条

$
0
0

用Apache工具做文件上传

1获得所接受文件要保存的路径

String path = getServletContext().getRealPath("/files");

2文件上传的临时目录,如不指定则为Tomcat/temps

File tempDir = new File("d:/a");

3创建用于解析文件的工厂类,同时设置缓冲区

注意这个类是Apache公司的jar包要导入两个jar包

        DiskFileItemFactory fileFactory = new DiskFileItemFactory(1024*8, tempDir);
        ServletFileUpload upload = new ServletFileUpload(fileFactory);

4设置每个上传文件最大值

upload.setFileSizeMax(1024*1024*5);

5设置所有上传文件大小的最大值

upload.setSizeMax(1024*1024*10);//10M

6开始解析–返回list集合

List<FileItem> list =  upload.parseRequest(request);

7分流:分为一般表单文件和上传文件

            if(item.isFormField()){//一般表单组件
                    String str = item.getString("utf-8");//以指定编码方式接受,以解决普通表单组件接受中文乱码
                    System.out.println("普通表单组件..."+str);
                }else{//文件组件

                }

8文件上传,文件拷贝

    FileUtils.copyInputStreamToFile(item.getInputStream(), new File(path+"/"+fileName2));

小细节:

服务器中文件名不能用中文,而且文件名又要唯一所以我们用UUID来做

                    String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                    String ext = fileName.substring(fileName.lastIndexOf("."));
                    String fileName2 = uuid+ext;

防黑处理1 在地址栏直接提交 ,则输出你请求的页面不支持get…

public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //防黑处理1 在地址栏直接提交 ,则输出你请求的页面不支持get...
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print("你请求的页面不支持此方式提交...");
    }

对于response.setContentType("text/html;charset=utf-8"); 而言如果是“multipart/form-data”表单,可以设置其中file组件的文件名,但对其中的普通表单组件无效。如果是我们以前用的“application/x-www-form-urlencoded”表单,可以设置其中的普通表单组件。

防黑2 非“multipart/form-data”表单提交

//法1: 手动解决
        String type = request.getContentType();
        if(!type.contains("multipart/form-data")){
            response.getWriter().print("此中提交方式不支持...");
            return;
        }
        //法2:使用上传工具解决
        boolean boo = ServletFileUpload.isMultipartContent(request);
        if(!boo){
            response.getWriter().print("此中提交方式不支持...");
            return;
        }

对于一般表单组件,以指定编码方式接受,以解决普通表单组件接受中文乱码

String str = item.getString("utf-8");

目录打散

目录打散我们是借助于fileName.hashCode 取每后四位做一层目录。16*16的目录方式来做。

                    String dir1 = Integer.toHexString(fileName.hashCode() & 0xf);
                    String dir2 = Integer.toHexString((fileName.hashCode() & 0xf0)>>4);
                    File dir = new File(path+"/"+dir1+"/"+dir2);
                    if(!dir.exists()){
                        dir.mkdirs();
                    }
                    File file = new File(dir+"/"+fileName2);

文件上传进度条简易版

用监听器监听,过多久刷一次。

upload.setProgressListener(new ProgressListener() {
            private int p=0;
            //readedBytes:已上传字节数   countBytes:上传的总字节数   count:文件序号(从1开始的)
            @Override
            public void update(long readedBytes, long countBytes, int count) {
                double d = readedBytes * 100.0 /countBytes;
                int dd = (int) d;
                if( dd != p ){
                    out.print("当前进度:"+dd+"<br/>");
                    p = dd;
                }
            }
        });

纯前台进度条

用两个div来做,通过JavaScript来设置里面那个div的width。

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <script type="text/javascript">
        function start(){
            a=0;
            time =  window.setInterval(run, 1000);
        }
        var a=0;
        function run(){
            a+=10;
            if(a>100){
                window.clearInterval(time);
                return;
            }
            var div = document.getElementById("divPro");
            div.style.width = a+"%";
        }
        function stop(){
            window.clearInterval(time);
        }
        function resume(){
            window.clearInterval(time);
            time =  window.setInterval(run, 1000);
        }

    </script>
  </head>

  <body>
        <h2>进度条演示</h2>
        <div style="border: 2px solid green; width: 300px;height: 30px; ">
            <div id="divPro" style="background:red; width:30%; height:100%"></div>
        </div>
        <button onclick="start()">启动</button>
        <button onclick="stop()">停止</button>
        <button onclick="resume()">恢复</button>
  </body>
</html>

本文完整代码如下:

index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
        <title>演示request和response用法</title>
  </head>
  <body>
  <!--通过request.getContextPath()可以把项目根目录(项目名)写活  -->
        <form action="<%=request.getContextPath() %>/requestDemo" method="post">
            姓名:<input type="text" name="name" /><br/>
            <input type="submit" value="提交"/>
        </form>
        <!-- 演示表单参数的接受 -->
        <form action="<%=request.getContextPath() %>/param" method="post">
            姓名:<input type="text" name="name" /><br/>
            年龄:<input type="text" name="age" /><br/>
            <input type="checkbox" name="hoby" value="music" />音乐&nbsp;&nbsp;
            <input type="checkbox" name="hoby" value="tv" />电视&nbsp;&nbsp;
            <input type="checkbox" name="hoby" value="game" />游戏<br/>
            <input type="radio" name="sex" value="0" checked="checked" >男&nbsp;&nbsp;
            <input type="radio" name="sex" value="1"><input type="submit" value="提交"/>
        </form>
        <!-- 演示文件上传 -->
        <h2>文件上传手动版</h2>
        <form action="<%=request.getContextPath() %>/upload1" method="post" enctype="multipart/form-data"> 
            文件名:<input type="file" name="fileName">
            <input type="submit" value="上传">
        </form>
        <!-- 上面这种手动上传 的方法不好。要自己解析,麻烦,我们用阿帕奇公司-Commons-io.jar 和 Commos-fileupload.jar写好的去做文件上传 -->

        <!-- 以下演示使用Apache公司的工具做上传 -->
        <h1>以下演示使用Apache公司的工具做上传  单项文件上传</h1>
        <form action="<%=request.getContextPath() %>/upload2" method="post" enctype="multipart/form-data"> 
            文件名:<input type="file" name="fileName">
            <input type="submit" value="上传">
        </form>
        <form action="<%=request.getContextPath() %>/upload2" method="post" enctype="multipart/form-data"> 
            文件1:<input type="file" name="fileName">
            文件1的说明:<input type="text" name="desc1">
            文件2:<input type="file" name="fileName2">
            文件2的说明:<input type="text" name="desc2">
            <input type="submit" value="上传">
        </form>
        <br/>
        <br/>
        <h1>完整点的文件上传</h1>
        <form action="<%=request.getContextPath() %>/upload3" method="post" enctype="multipart/form-data"> 
            文件1:<input type="file" name="fileName">
            文件1的说明:<input type="text" name="desc1">
            文件2:<input type="file" name="fileName2">
            文件2的说明:<input type="text" name="desc2">
            <input type="submit" value="上传">
        </form>
        <br/><br/>
        <a href = "progress.jsp">纯前台的进度条</a>

  </body>
</html>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name>
  <servlet>
    <servlet-name>RequestDemo</servlet-name>
    <servlet-class>cn.hncu.servlets.RequestDemo</servlet-class>
    <init-param>
        <param-name>name</param-name>
        <param-value>Hello</param-value>
    </init-param>
  </servlet>
  <servlet>
    <servlet-name>ParamerServlet</servlet-name>
    <servlet-class>cn.hncu.servlets.ParamerServlet</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>UpFileServlet</servlet-name>
    <servlet-class>cn.hncu.servlets.UpFileServlet</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>cn.hncu.servlets.upload.UploadServlet</servlet-class>
  </servlet>
  <servlet>
    <servlet-name>UpLoadServlet3</servlet-name>
    <servlet-class>cn.hncu.servlets.upload.UpLoadServlet3</servlet-class>
  </servlet>





  <servlet-mapping>
    <servlet-name>RequestDemo</servlet-name>
    <url-pattern>/requestDemo</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>ParamerServlet</servlet-name>
    <url-pattern>/param</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>UpFileServlet</servlet-name>
    <url-pattern>/upload1</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/upload2</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>UpLoadServlet3</servlet-name>
    <url-pattern>/upload3</url-pattern>
  </servlet-mapping>    
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

UploadServlet

package cn.hncu.servlets.upload;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;

public class UploadServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //防黑处理1 在地址栏直接提交 ,则输出你请求的页面不支持get...
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print("你请求的页面不支持此方式提交...");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");

        //注意,下面这句设置中文,如果是“multipart/form-data”表单,可以设置其中file组件的文件名,但对其中的普通表单组件无效。
        //注意,下面这句设置中文,如果是我们以前用的“application/x-www-form-urlencoded”表单,可以设置其中的普通表单组件。
        response.setContentType("text/html;charset=utf-8");
        //1上传文件的保存路径
        String path = getServletContext().getRealPath("/files");

        //防黑2 非“multipart/form-data”表单提交
        //法1: 手动解决
//      String type = request.getContentType();
//      if(!type.contains("multipart/form-data")){
//          response.getWriter().print("此中提交方式不支持...");
//          return;
//      }
        //法2:使用上传工具解决
        boolean boo = ServletFileUpload.isMultipartContent(request);
        if(!boo){
            response.getWriter().print("此中提交方式不支持...");
            return;
        }



        //2文件上传的临时目录,如不指定则为tomcat/temps
        File tempDir = new File("d:/a");
        //3创建用于解析文件的工厂类,同时设置缓冲区大小和位置
        DiskFileItemFactory fileFactory = new DiskFileItemFactory(1024*8, tempDir);
        ServletFileUpload upload = new ServletFileUpload(fileFactory);
        //4设置每个文件的最大值
        upload.setFileSizeMax(1024*1024*5);
        //5设置所有文件大小之和的最大值
        upload.setSizeMax(1024*1024*10);//10M
        //6开始解析request--->返回list集合
        try {
            List<FileItem> list =  upload.parseRequest(request);
            //7分流:分为文件组件和一般表单组件
            for(FileItem item : list) {
                if(item.isFormField()){//一般表单组件
                    String str = item.getString("utf-8");//以指定编码方式接受,以解决普通表单组件接受中文乱码
                    System.out.println("普通表单组件..."+str);
                }else{//文件组件
                    //防黑3 用户不选择文件提交
                    if(item.getSize()==0){
                        continue;
                    }
                    //文件名
                    String fileName = item.getName();
                    //服务器中名字不能用中文,但是文件名又要唯一我们用UUID来做
                    System.out.println("上传文件为:"+fileName);
                    String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                    String ext = fileName.substring(fileName.lastIndexOf("."));
                    String fileName2 = uuid+ext;
//                  System.out.println(path);
//                  System.out.println(fileName2);
                    //Apache公司自己帮我们做好了写文件的工具
                    //真正的文件内容在 item.getInputStream() 当中
                    FileUtils.copyInputStreamToFile(item.getInputStream(), new File(path+"/"+fileName2));
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }

}

UpLoadServlet3

package cn.hncu.servlets.upload;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;

public class UpLoadServlet3 extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //防黑处理1 在地址栏直接提交 ,则输出你请求的页面不支持get...
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().print("你请求的页面不支持此方式提交...");
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        //注意,下面这句设置中文,如果是“multipart/form-data”表单,可以设置其中file组件的文件名,但对其中的普通表单组件无效。
        //注意,下面这句设置中文,如果是我们以前用的“application/x-www-form-urlencoded”表单,可以设置其中的普通表单组件。
        response.setContentType("text/html;charset=utf-8");
        final PrintWriter out = response.getWriter();
        //1上传文件的保存路径
        String path = getServletContext().getRealPath("/files");

        //防黑2 非“multipart/form-data”表单提交
        //法1: 手动解决
//      String type = request.getContentType();
//      if(!type.contains("multipart/form-data")){
//          response.getWriter().print("此中提交方式不支持...");
//          return;
//      }
        //法2:使用上传工具解决
        boolean boo = ServletFileUpload.isMultipartContent(request);
        if(!boo){
            response.getWriter().print("此中提交方式不支持...");
            return;
        }



        //2文件上传的临时目录,如不指定则为tomcat/temps
        File tempDir = new File("d:/a");
        //3创建用于解析文件的工厂类,同时设置缓冲区大小和位置
        DiskFileItemFactory fileFactory = new DiskFileItemFactory(1024*8, tempDir);
        ServletFileUpload upload = new ServletFileUpload(fileFactory);
        //4设置每个文件的最大值
        upload.setFileSizeMax(1024*1024*5);
        //5设置所有文件大小之和的最大值
        upload.setSizeMax(1024*1024*10);//10M

        //文件上传进度条基础版
        upload.setProgressListener(new ProgressListener() {
            private int p=0;
            //readedBytes:已上传字节数   countBytes:上传的总字节数   count:文件序号(从1开始的)
            @Override
            public void update(long readedBytes, long countBytes, int count) {
                double d = readedBytes * 100.0 /countBytes;
                int dd = (int) d;
                if( dd != p ){
                    out.print("当前进度:"+dd+"<br/>");
                    p = dd;
                }
            }
        });



        //6开始解析request--->返回list集合
        try {
            List<FileItem> list =  upload.parseRequest(request);
            //7分流:分为文件组件和一般表单组件
            for(FileItem item : list) {
                if(item.isFormField()){//一般表单组件
                    String str = item.getString("utf-8");//以指定编码方式接受,以解决普通表单组件接受中文乱码
                    System.out.println("普通表单组件..."+str);
                }else{//文件组件
                    //防黑3 用户不选择文件提交
                    if(item.getSize()==0){
                        continue;
                    }
                    //文件名
                    String fileName = item.getName();
                    fileName = fileName.substring(fileName.lastIndexOf("/")+1);
                    //服务器中名字不能用中文,但是文件名又要唯一我们用UUID来做
                    System.out.println("上传文件为:"+fileName);
                    String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                    String ext = fileName.substring(fileName.lastIndexOf("."));
                    String fileName2 = uuid+ext;

                    //打散目录
                    String dir1 = Integer.toHexString(fileName.hashCode() & 0xf);
                    String dir2 = Integer.toHexString((fileName.hashCode() & 0xf0)>>4);
                    File dir = new File(path+"/"+dir1+"/"+dir2);
                    if(!dir.exists()){
                        dir.mkdirs();
                    }
                    File file = new File(dir+"/"+fileName2);

//                  System.out.println(path);
//                  System.out.println(fileName2);
                    //Apache公司自己帮我们做好了写文件的工具
                    //真正的文件内容在 item.getInputStream() 当中
                    //FileUtils.copyInputStreamToFile(item.getInputStream(), new File(path+"/"+fileName2));
                    try {
                        item.write(file);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }

}

progress.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <script type="text/javascript">
        function start(){
            a=0;
            time =  window.setInterval(run, 1000);
        }
        var a=0;
        function run(){
            a+=10;
            if(a>100){
                window.clearInterval(time);
                return;
            }
            var div = document.getElementById("divPro");
            div.style.width = a+"%";
        }
        function stop(){
            window.clearInterval(time);
        }
        function resume(){
            window.clearInterval(time);
            time =  window.setInterval(run, 1000);
        }

    </script>
  </head>

  <body>
        <h2>进度条演示</h2>
        <div style="border: 2px solid green; width: 300px;height: 30px; ">
            <div id="divPro" style="background:red; width:30%; height:100%"></div>
        </div>
        <button onclick="start()">启动</button>
        <button onclick="stop()">停止</button>
        <button onclick="resume()">恢复</button>
  </body>
</html>
作者:new___Smile 发表于2016/7/22 22:22:51 原文链接
阅读:44 评论:0 查看评论

JAVA 面向对象 隐藏和封装

$
0
0

本页面更新日期: 2016年07月22日

前言

在前面程序中,经常出现通过某个对象直接访问其成员变量的情况.
这可能引起一些潜在问题,比如将某个 Person 的 age 成员变量直接设为 1000.
这在语法上没有任何问题, 但显然违背了当前的自然规律. 人怎么可能活到 1000岁 - - . (就现在的科学来讲)
Java也考虑到了这种情况, 为你提供了 类和对象的成员变量进行封装的方法,来保护成员变量不被恶意修改.

理解封装

封装(Encapsulation)是面向对象的三大特征之一.(另外两个是继承和多态, 以后会讲到)
它指的是将对象的状态信息隐藏在对应内部.
不允许外部程序直接访问对象内部信息.
而是通过该类所提供的方法来实现对内部信息的操作和访问.

封装是面向对象编程语言对客观世界的模拟
在客观世界里, 对象的状态信息都是被隐藏在对象内部.
外界无法直接操作和修改.
就如刚刚说的 Person 对象的 age 变量, 只能随着岁月的流逝, age 才会增加.
通常不能随意修改 Person 对象的 age.
对一个类或对象实现良好的封装, 可以实现以下目的.

  • 隐藏类的实现细节.
  • 让使用者只能通过事先预定的方法来访问数据, 从而可以在该方法里加入控制逻辑,限制对成员变量的不合理访问.
  • 可进行数据检查, 从而有利于保证对象信息的完整性.
  • 便于修改,提高代码的可维护性.

为了实现良好的封装, 需要从两个方面考虑.

  • 将对象的成员变量和实现细节隐藏起来, 不允许外部直接访问.
  • 把方法暴露出来, 让方法来控制对这些成员变量进行安全的访问和操作.

因此, 封装实际上有两个方面的含义:
把该隐藏的隐藏起来.
把该暴露的暴露出来.
这两个方面都需要通过使用 Java 提供的访问控制符来实现.

使用访问控制符

Java 提供了 3 个访问控制符.

  • private
  • protected
  • public

它们分别代表了 3 个 访问控制级别.
另外还有一个不加任何访问控制符的访问控制级别.
所以一共 4 个访问控制级别.
Java 的访问控制级别由小到大如下图所示:

这里写图片描述

上图的 4 个访问控制级别中. default 并没有对应的访问控制符.
当你不使用任何访问控制符来修饰类或类成员时, 系统默认使用该访问控制级别.
这 4 个访问控制级别的详细介绍如下:

  • private(当前类访问权限): 如果类里的一个成员(包括成员变量 / 方法 / 和构造器等)使用 private 访问控制符来修饰. 则这个成员只能在当前类的内部被访问. 很显然, 这个访问控制符用于修饰成员变量最合适. 使用它来修饰成员变量就可以把成员变量隐藏在该类的内部.
  • default(包访问权限):如果类里的一个成员(包括成员变量 / 方法 / 和构造器等) 或者一个外部类不使用任何访问控制修饰符,就称它是包访问权限的. 什么是包? 以后会讲到. default 访问控制的成员或外部类可以被相同包下的其它类访问.
  • protected(子类访问权限): 如果一个成员(包括成员变量 / 方法 / 和构造器等)使用 protected 访问控制符修饰, 那么这个成员既可以被同一个包中的其它类访问, 也可以被不同包中的子类访问. 在通常情况下, 如果使用 protected 来修饰一个方法, 通常是希望其子类来重写这个方法. 关于父类 / 子类 我以后会讲到.
  • public(公共访问权限): 这是一个最宽松的访问控制级别.如果一个成员(包括成员变量 / 方法 / 和构造器等) 或者一个外部类使用 public 访问控制符修饰, 那么这个成员或外部类就可以被所有类访问,不管访问类和被访问类是否处于同一个包中, 是否具有父子继承关系.

通过上面的解释不难发现, 访问控制符用于控制一个类的成员是否可以被其它类访问.
对于局部变量而言, 其作用域就是它所在的方法, 不可能被其它类访问, 因此不能使用访问控制符来修饰.

对于外部类而言,它也可以使用访问控制符修饰,但外部类只能有两种访问控制级别

  • public
  • 和默认 (default)

因为外部类没有处于任何类的内部, 也就没有其所在类的内部 / 所在类的子类 两个范围, 因此 private 和 protected 访问控制符对外部类没有任何意义.

外部类可以使用 public 和 包访问控制权限.
使用 public 修饰的外部类可以被所有类使用.
如声明变量 / 创建实例.
不使用任何访问控制符修饰的外部类只能被同一个包中的其它类使用.

小插曲: 如果一个 Java 源文件里定义的所有类都没有使用 public 修饰, 则这个 Java 源文件的文件名可以是一切合法的文件名.
但如果一个 Java 源文件里定义了一个 public 修饰的类, 则这个源文件的文件名必需与public 修饰的类的类名相同.

明白了访问控制符的用法后, 下面通过使用合理的访问控制符来定义一个 Person 类, 这个 Person 类实现了良好的封装.

public class Person
{
  //使用 private 修饰成员变量, 将这些成员变量隐藏起来
  private String name;
  private int age;
  //提供方法来操作 name 成员变量
  public void setName(String name)
  {
    //执行合理性验证,要求用户名必需在 2~6 位之间.
    if(name.length() > 6 || name.length() < 2)
    {
      System.out.println("您设置的人名不符合要求!");
      return;
    }
    else
    {
      this.name = name;
    }
  }
  //提供方法来操作 age 成员变量
  public void setAge(int age)
  {
    //执行合理性验证,要求用户年龄必须在 0~100 之间
    if(age > 100 || age < 0)
    {
      System.out.println("您设置的年龄不合法!");
      return;
    }
    else
    {
      this.age = age;
    }
  }
}

定义了上面的 Person 类之后, 该类的 name 和 age 两个成员变量只有在 Person 类的内部 才可以操作和访问.
在 Person 类之外只能通过其对应的 setter 和 getter 方法来操作和访问它们.

小知识:
Java 类里实例变量的 setter 和 getter 方法有非常重要的意义.
例如, 某个类里包含了一个名为 abc 的实例变量, 则其对应的 setter 和 getter 方法名为 setAbc() 和 getAbc()
即将原实例变量名的首字母大写, 并在前面分别增加 set 和 get 动词.
就变成了 setter 和 getter 方法名.
如果一个 Java 类的每个实例变量都被使用 private 修饰, 并为每个实例变量都提供了 public 修饰的 setter 和 getter 方法, 那么这个类就是一个 符合 JavaBean规范的类.
因此, JavaBean 总是一个封装良好的类.

下面程序在 main()方法中创建一个 Person 对象, 并尝试操作和访问该对象的 age 和 name 两个实例变量.

public class PersonTest
{
  public static void main(String[] args)
  {
    Person p = new Person();
    //因为 age 成员变量已被隐藏, 所以下面语句将会出现编译错误
    // p.age = 1000;
    //下面语句不会出现错误, 但运行时将会得到违规提示
    //同时程序不会修改 age 成员变量
    p.setAge(1000);
    //访问 p 的 age 成员变量也必需通过其对应的 getter 方法
    //因为上面从未成功设置 p 的 age 成员变量, 故此处会输出 0
    System.out.println(p.getAge());
    //下面成功修改 p 的 age 成员变量
    p.setAge(30);
    //因为上面成功设置了 p 的 age 成员变量, 故此处输出 30
    Sytem.out.println(p.getAge());
    //不能直接操作 p 的 name 成员变量, 只能通过其对应的 setter 方法
    //因为 "孙悟空" 字符串长度满足 2~6 的要求, 所以可以成功设置
    p.setName("孙悟空");
    System.out.println(p.getName());
  }
}

现在你已经创建了两个类文件 一个是 Person , 一个是 PersonTest
如果你编译它们并直接运行 PersonTest 是会出错的, 原因是他俩并没有产生关联. 不过暂时你先看看代码的逻辑就行, 以后我们会讲到如何成功运行这俩个类.

观察上面程序可得出, PersonTest 类的 main() 方法bu可以直接修改 Person 对象的 name 和 age 两个实例变量.
只能通过各自对应的 setter 方法来操作这两个实例变量的值.
因为使用 setter 方法来操作 name 和 age 两个实例变量, 就允许程序员在setter 方法中增加自己的控制逻辑.
从而保证 Person 对象的 name 和 age 两个实例变量不会出现与实际不符的情形.

小知识:
一个类常常就是一个小的模块.
应该只让这个模块公开必需让外界知道的内容.
隐藏其他一切内容.
进行程序设计时, 应尽量避免让一个模块直接操作和访问另一个模块的数据.
模块设计追求高内聚(尽可能把模块的内部数据 / 功能实现 细节隐藏在模块内部独立完成, 不允许外部直接干预)
低耦合(仅暴露少量的方法给外部使用)
正如日常常见的内存条, 内存里的数据及其实现细节被完全隐藏在内存条里.
外部设备(如主板)
只能通过内存条的金手指(提供一些方法供外部调用)来和内存条进行交互.

关于访问控制符的使用, 如下几个原则你要知道.

  • 类里的绝大部分成员变量都应该使用 private 修饰, 只有一些 static 修饰的 / 类似全局变量的成员变量, 才考虑使用 public 修饰. 除此之外, 有些方法只用于辅助实现该类的其它方法, 这些方法被称为工具方法,工具方法也应该使用 private 修饰.
  • 如果某个类主要用作其它类的父类, 该类里包含的大部分方法可能仅希望被子类重写, 而不想被外界直接调用, 则应该使用 protected 修饰这些方法.
  • 希望暴露出来给其它类自由调用的方法应该使用 public 修饰. 因此, 类的构造器通常使用 public 修饰, 从而允许在其它地方创建该类的实例. 因为外部类通常都希望被其他类自由使用. 所以, 大部分外部类都使用 public 修饰.

小提示:
在教学当中, 有些代码示例可能并未进行良好的封装, 当你自己开发项目时, 请尽量注意. 我只是为了演示某个知识点而故意偷点懒.嘿嘿嘿.

作者:tmdlife 发表于2016/7/22 22:25:11 原文链接
阅读:43 评论:0 查看评论

HTTP请求响应报文&&相关状态码&&GET_POST请求方法 总结

$
0
0

HTTP请求报文:

一个HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据

1.请求行  

请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。比如 GET /data/info.html HTTP/1.1

方法字段就是HTTP使用的请求方法,比如常见的GET/POST

其中HTTP协议版本有两种:HTTP1.0/HTTP1.1 可以这样区别:

HTTP1.0对于每个连接都的建立一次连接一次只能传送一个请求和响应,请求就会关闭,HTTP1.0没有Host字段;
而HTTP1.1在同一个连接中可以传送多个请求和响应,多个请求可以重叠和同时进行,HTTP1.1必须有Host字段。

 

2.请求头部

请求头部是 字段名:值 的格式展现

用于HTTP请求中的常用请求头字段
Accept:       用于高速服务器,客户机支持的数据类型
Accept-Charset:  用于告诉服务器,客户机采用的编码格式
Accept-Encoding:用于告诉服务器,客户机支持的数据压缩格式
Accept-Language:客户机的语言环境
Host:      客户机通过这个头高速服务器,想访问的主机名
If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间
Referer:      客户机通过这个头告诉服务器,它是从哪个资源来访问服务器的(防盗链)
User-Agent:    客户机通过这个头告诉服务器,客户机的软件环境
Cookie:       客户机通过这个头可以向服务器带数据
Connection:    处理完这次请求后是否断开连接还是继续保持连接
Date:        当前时间值

 

比如通过百度的GET某张图片--其相应请求头部


     

3.空行

空行的表现形式很直接,就是一个空行

它的作用是通过一个空行,告诉服务器请求头部到此为止

 

4.请求数据

若方法字段是GET,则此项为空,没有数据

若方法字段是POST,则通常来说此处放置的就是要提交的数据  

比如要使用POST方法提交一个表单,其中有name字段中数据为“xiaoming",age字段为17

那么这里的请求数据就是  name=xiaoming&age=17    使用&来连接各个字段

 

    总的来说,HTTP请求报文 就如同这张图所示-->


 

一个稍微完整的HTTP请求报文:


上面是POST方法,它的请求行URL段中一般是没有参数的,参数放在了报文体中

接下来看看GET方法,参数直接置于请求行URL中,报文体则为空

假设通过get方法,产生了这么一个链接:

<a href="http://www.google.cn/search?hl=zh-CN&source=hp&q=domety&aq=f&oq=">http://www.google.cn/search?hl=zh-CN&source=hp
&q=domety&aq=f&oq=</a> 

则相应的请求报文为:

GET /search?hl=zh-CN&source=hp&q=domety&aq=f&oq= HTTP/1.1  
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, 
application/msword, application/x-silverlight, application/x-shockwave-flash, */*  
Referer: <a href="http://www.google.cn/">http://www.google.cn/</a>  
Accept-Language: zh-cn  
Accept-Encoding: gzip, deflate  
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; TheWorld)  
Host: <a href="http://www.google.cn">www.google.cn</a>  
Connection: Keep-Alive  
Cookie: PREF=ID=80a06da87be9ae3c:U=f7167333e2c3b714:NW=1:TM=1261551909:LM=1261551917:S=ybYcq2wpfefs4V9g; 
NID=31=ojj8d-IygaEtSxLgaJmqSjVhCspkviJrB6omjamNrSm8lZhKy_yMfO2M4QMRKcH1g0iQv9u-2hfBW7bUFwVh7pGaRUb0RnHcJU37y-
FxlRugatx63JLv7CWMD6UB_O_r

 

            HTTP响应报文:

同样的,HTTP响应报文也由三部分组成:响应行、响应头、响应体

1.响应行

响应行一般由协议版本、状态码及其描述组成   比如 HTTP/1.1 200 OK

其中协议版本HTTP/1.1 或者HTTP/1.0

200就是它的状态码,OK则为它的描述

----------------------常见状态码---------------

 100~199:表示成功接收请求,要求客户端继续提交下一次请求才能完成整个处理过程。
 200~299:表示成功接收请求并已完成整个处理过程。常用200
 300~399:为完成请求,客户需进一步细化请求。例如:请求的资源已经移动一个新地址、常用302(意味着你请求我,我让你去找别人),307和304(我不给你这个资源,自己拿缓存)
 400~499:客户端的请求有错误,常用404(意味着你请求的资源在web服务器中没有)403(服务器拒绝访问,权限不够)
 500~599:服务器端出现错误,常用500

还有引自w3school的说明


【1xx: 信息】

100 Continue    服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。
101 Switching Protocols    服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。

【2xx: 成功】

200 OK    请求成功(其后是对GET和POST请求的应答文档。)
201 Created    请求被创建完成,同时新的资源被创建。
202 Accepted    供处理的请求已被接受,但是处理未完成。
203 Non-authoritative Information    文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝。
204 No Content    没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。
205 Reset Content    没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。
206 Partial Content    客户发送了一个带有Range头的GET请求,服务器完成了它。

【3xx: 重定向】

300 Multiple Choices    多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。
301 Moved Permanently    所请求的页面已经转移至新的url。
302 Found    所请求的页面已经临时转移至新的url。
303 See Other    所请求的页面可在别的url下被找到。
304 Not Modified    未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
305 Use Proxy    客户请求的文档应该通过Location头所指明的代理服务器提取。
306 Unused    此代码被用于前一版本。目前已不再使用,但是代码依然被保留。
307 Temporary Redirect    被请求的页面已经临时移至新的url。

【4xx: 客户端错误】

400 Bad Request    服务器未能理解请求。
401 Unauthorized    被请求的页面需要用户名和密码。
402 Payment Required    此代码尚无法使用。
403 Forbidden    对被请求页面的访问被禁止。
404 Not Found    服务器无法找到被请求的页面。
405 Method Not Allowed    请求中指定的方法不被允许。
406 Not Acceptable    服务器生成的响应无法被客户端所接受。
407 Proxy Authentication Required    用户必须首先使用代理服务器进行验证,这样请求才会被处理。
408 Request Timeout    请求超出了服务器的等待时间。
409 Conflict    由于冲突,请求无法被完成。
410 Gone    被请求的页面不可用。
411 Length Required    "Content-Length" 未被定义。如果无此内容,服务器不会接受请求。
412 Precondition Failed    请求中的前提条件被服务器评估为失败。
413 Request Entity Too Large    由于所请求的实体的太大,服务器不会接受请求。
414 Request-url Too Long    由于url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种情况。
415 Unsupported Media Type    由于媒介类型不被支持,服务器不会接受请求。
416     服务器不能满足客户在请求中指定的Range头。
417 Expectation Failed     

【5xx: 服务器错误】

500 Internal Server Error    请求未完成。服务器遇到不可预知的情况。
501 Not Implemented    请求未完成。服务器不支持所请求的功能。
502 Bad Gateway    请求未完成。服务器从上游服务器收到一个无效的响应。
503 Service Unavailable    请求未完成。服务器临时过载或当机。
504 Gateway Timeout    网关超时。
505 HTTP Version Not Supported    服务器不支持请求中指明的HTTP协议版本。
View Code

 

更详细全面的状态码:


  1 状态码    含义 原文(http://tool.oschina.net/commons?type=5)
  2 
  3 100    客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。
  4 
  5 101    服务器已经理解了客户端的请求,并将通过Upgrade 消息头通知客户端采用不同的协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到在Upgrade 消息头中定义的那些协议。   只有在切换新的协议更有好处的时候才应该采取类似措施。例如,切换到新的HTTP 版本比旧版本更有优势,或者切换到一个实时且同步的协议以传送利用此类特性的资源。
  6 
  7 102    由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。
  8 
  9 200    请求已成功,请求所希望的响应头或数据体将随此响应返回。
 10 
 11 201    请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经随Location 头信息返回。假如需要的资源无法及时建立的话,应当返回 202 Accepted 12 
 13 202    服务器已接受请求,但尚未处理。正如它可能被拒绝一样,最终该请求可能会也可能不会被执行。在异步操作的场合下,没有比发送这个状态码更方便的做法了。   返回202状态码的响应的目的是允许服务器接受其他过程的请求(例如某个每天只执行一次的基于批处理的操作),而不必让客户端一直保持与服务器的连接直到批处理操作全部完成。在接受请求处理并返回202状态码的响应应当在返回的实体中包含一些指示处理当前状态的信息,以及指向处理状态监视器或状态预测的指针,以便用户能够估计操作是否已经完成。
 14 
 15 203    服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。当前的信息可能是原始版本的子集或者超集。例如,包含资源的元数据可能导致原始服务器知道元信息的超级。使用此状态码不是必须的,而且只有在响应不使用此状态码便会返回200 OK的情况下才是合适的。
 16 
 17 204    服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。响应可能通过实体头部的形式,返回新的或更新后的元信息。如果存在这些头部信息,则应当与所请求的变量相呼应。   如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息应当被应用到用户浏览器活动视图中的文档。   由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾。
 18 
 19 205    服务器成功处理了请求,且没有返回任何内容。但是与204响应不同,返回此状态码的响应要求请求者重置文档视图。该响应主要是被用于接受用户输入后,立即重置表单,以便用户能够轻松地开始另一次输入。   与204响应一样,该响应也被禁止包含任何消息体,且以消息头后的第一个空行结束。
 20 
 21 206    服务器已经成功处理了部分 GET 请求。类似于 FlashGet 或者迅雷这类的 HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。   该请求必须包含 Range 头信息来指示客户端希望得到的内容范围,并且可能包含 If-Range 来作为请求条件。   响应必须包含如下的头部域:   Content-Range 用以指示本次响应中返回的内容的范围;如果是 Content-Type 为 multipart/byteranges 的多段下载,则每一 multipart 段中都应包含 Content-Range 域用以指示本段的内容范围。假如响应中包含 Content-Length,那么它的数值必须匹配它返回的内容范围的真实字节数。   Date   ETag 和/或 Content-Location,假如同样的请求本应该返回200响应。   Expires, Cache-Control,和/或 Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。   假如本响应请求使用了 If-Range 强缓存验证,那么本次响应不应该包含其他实体头;假如本响应的请求使用了 If-Range 弱缓存验证,那么本次响应禁止包含其他实体头;这避免了缓存的实体内容和更新了的实体头信息之间的不一致。否则,本响应就应当包含所有本应该返回200响应中应当返回的所有实体头部域。   假如 ETag 或 Last-Modified 头部不能精确匹配的话,则客户端缓存应禁止将206响应返回的内容与之前任何缓存过的内容组合在一起。   任何不支持 Range 以及 Content-Range 头的缓存都禁止缓存206响应返回的内容。
 22 
 23 207    由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码。
 24 
 25 300    被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向。   除非这是一个 HEAD 请求,否则该响应应当包括一个资源特性及地址的列表的实体,以便用户或浏览器从中选择最合适的重定向地址。这个实体的格式由 Content-Type 定义的格式所决定。浏览器可能根据响应的格式以及浏览器自身能力,自动作出最合适的选择。当然,RFC 2616规范并没有规定这样的自动选择该如何进行。   如果服务器本身已经有了首选的回馈选择,那么在 Location 中应当指明这个回馈的 URI;浏览器可能会将这个 Location 值作为自动重定向的地址。此外,除非额外指定,否则这个响应也是可缓存的。
 26 
 27 301    被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。   新的永久性的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。   如果这不是一个 GET 或者 HEAD 请求,因此浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。   注意:对于某些使用 HTTP/1.0 协议的浏览器,当它们发送的 POST 请求得到了一个301响应的话,接下来的重定向请求将会变成 GET 方式。
 28 
 29 302    请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。   新的临时性的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。   如果这不是一个 GET 或者 HEAD 请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。   注意:虽然RFC 1945和RFC 2068规范不允许客户端在重定向时改变请求的方法,但是很多现存的浏览器将302响应视作为303响应,并且使用 GET 方式访问在 Location 中规定的 URI,而无视原先请求的方法。状态码303和307被添加了进来,用以明确服务器期待客户端进行何种反应。
 30 
 31 303    对应当前请求的响应可以在另一个 URI 上被找到,而且客户端应当采用 GET 的方式访问那个资源。这个方法的存在主要是为了允许由脚本激活的POST请求输出重定向到一个新的资源。这个新的 URI 不是原始资源的替代引用。同时,303响应禁止被缓存。当然,第二个请求(重定向)可能被缓存。   新的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。   注意:许多 HTTP/1.1 版以前的 浏览器不能正确理解303状态。如果需要考虑与这些浏览器之间的互动,302状态码应该可以胜任,因为大多数的浏览器处理302响应时的方式恰恰就是上述规范要求客户端处理303响应时应当做的。
 32 
 33 304    如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息头后的第一个空行结尾。   该响应必须包含以下的头信息:   Date,除非这个服务器没有时钟。假如没有时钟的服务器也遵守这些规则,那么代理服务器以及客户端可以自行将 Date 字段添加到接收到的响应头中去(正如RFC 2068中规定的一样),缓存机制将会正常工作。   ETag 和/或 Content-Location,假如同样的请求本应返回200响应。   Expires, Cache-Control,和/或Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。   假如本响应请求使用了强缓存验证,那么本次响应不应该包含其他实体头;否则(例如,某个带条件的 GET 请求使用了弱缓存验证),本次响应禁止包含其他实体头;这避免了缓存了的实体内容和更新了的实体头信息之间的不一致。   假如某个304响应指明了当前某个实体没有缓存,那么缓存系统必须忽视这个响应,并且重复发送不包含限制条件的请求。   假如接收到一个要求更新某个缓存条目的304响应,那么缓存系统必须更新整个条目以反映所有在响应中被更新的字段的值。
 34 
 35 305    被请求的资源必须通过指定的代理才能被访问。Location 域中将给出指定的代理所
 36 在的 URI 信息,接收者需要重复发送一个单独的请求,通过这个代理才能访问相应资源。只有原始服务器才能建立305响应。   注意:RFC 2068中没有明确305响应是为了重定向一个单独的请求,而且只能被原始服务器建立。忽视这些限制可能导致严重的安全后果。
 37 
 38 306    在最新版的规范中,306状态码已经不再被使用。
 39 
 40 307    请求的资源现在临时从不同的URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。   新的临时性的URI 应当在响应的 Location 域中返回。除非这是一个HEAD 请求,否则响应的实体中应当包含指向新的URI 的超链接及简短说明。因为部分浏览器不能识别307响应,因此需要添加上述必要信息以便用户能够理解并向新的 URI 发出访问请求。   如果这不是一个GET 或者 HEAD 请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。
 41 
 42 400    1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。   2、请求参数有误。
 43 
 44 401    当前请求需要用户验证。该响应必须包含一个适用于被请求资源的 WWW-Authenticate 信息头用以询问用户信息。客户端可以重复提交一个包含恰当的 Authorization 头信息的请求。如果当前请求已经包含了 Authorization 证书,那么401响应代表着服务器验证已经拒绝了那些证书。如果401响应包含了与前一个响应相同的身份验证询问,且浏览器已经至少尝试了一次验证,那么浏览器应当向用户展示响应中包含的实体信息,因为这个实体信息中可能包含了相关诊断信息。参见RFC 2617 45 
 46 402    该状态码是为了将来可能的需求而预留的。
 47 
 48 403    服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。如果这不是一个 HEAD 请求,而且服务器希望能够讲清楚为何请求不能被执行,那么就应该在实体内描述拒绝的原因。当然服务器也可以返回一个404响应,假如它不希望让客户端获得任何信息。
 49 
 50 404    请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。
 51 
 52 405    请求行中指定的请求方法不能被用于请求相应的资源。该响应必须返回一个Allow 头信息用以表示出当前资源能够接受的请求方法的列表。   鉴于 PUT,DELETE 方法会对服务器上的资源进行写操作,因而绝大部分的网页服务器都不支持或者在默认配置下不允许上述请求方法,对于此类请求均会返回405错误。
 53 
 54 406    请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。   除非这是一个 HEAD 请求,否则该响应就应当返回一个包含可以让用户或者浏览器从中选择最合适的实体特性以及地址列表的实体。实体的格式由 Content-Type 头中定义的媒体类型决定。浏览器可以根据格式及自身能力自行作出最佳选择。但是,规范中并没有定义任何作出此类自动选择的标准。
 55 
 56 407     与401响应类似,只不过客户端必须在代理服务器上进行身份验证。代理服务器必须返回一个 Proxy-Authenticate 用以进行身份询问。客户端可以返回一个 Proxy-Authorization 信息头用以验证。参见RFC 2617 57 
 58 408    请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送。客户端可以随时再次提交这一请求而无需进行任何更改。
 59 
 60 409    由于和被请求的资源的当前状态之间存在冲突,请求无法完成。这个代码只允许用在这样的情况下才能被使用:用户被认为能够解决冲突,并且会重新提交新的请求。该响应应当包含足够的信息以便用户发现冲突的源头。   冲突通常发生于对 PUT 请求的处理中。例如,在采用版本检查的环境下,某次 PUT 提交的对特定资源的修改请求所附带的版本信息与之前的某个(第三方)请求向冲突,那么此时服务器就应该返回一个409错误,告知用户请求无法完成。此时,响应实体中很可能会包含两个冲突版本之间的差异比较,以便用户重新提交归并以后的新版本。
 61 
 62 410    被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。这样的状况应当被认为是永久性的。如果可能,拥有链接编辑功能的客户端应当在获得用户许可后删除所有指向这个地址的引用。如果服务器不知道或者无法确定这个状况是否是永久的,那么就应该使用404状态码。除非额外说明,否则这个响应是可缓存的。   410响应的目的主要是帮助网站管理员维护网站,通知用户该资源已经不再可用,并且服务器拥有者希望所有指向这个资源的远端连接也被删除。这类事件在限时、增值服务中很普遍。同样,410响应也被用于通知客户端在当前服务器站点上,原本属于某个个人的资源已经不再可用。当然,是否需要把所有永久不可用的资源标记为410 Gone,以及是否需要保持此标记多长时间,完全取决于服务器拥有者。
 63 
 64 411    服务器拒绝在没有定义 Content-Length 头的情况下接受请求。在添加了表明请求消息体长度的有效 Content-Length 头之后,客户端可以再次提交该请求。
 65 
 66 412    服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。这个状态码允许客户端在获取资源时在请求的元信息(请求头字段数据)中设置先决条件,以此避免该请求方法被应用到其希望的内容以外的资源上。
 67 
 68 413    服务器拒绝处理当前请求,因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围。此种情况下,服务器可以关闭连接以免客户端继续发送此请求。   如果这个状况是临时的,服务器应当返回一个 Retry-After 的响应头,以告知客户端可以在多少时间以后重新尝试。
 69 
 70 414    请求的URI 长度超过了服务器能够解释的长度,因此服务器拒绝对该请求提供服务。这比较少见,通常的情况包括:   本应使用POST方法的表单提交变成了GET方法,导致查询字符串(Query String)过长。   重定向URI “黑洞”,例如每次重定向把旧的 URI 作为新的 URI 的一部分,导致在若干次重定向后 URI 超长。   客户端正在尝试利用某些服务器中存在的安全漏洞攻击服务器。这类服务器使用固定长度的缓冲读取或操作请求的 URI,当 GET 后的参数超过某个数值后,可能会产生缓冲区溢出,导致任意代码被执行[1]。没有此类漏洞的服务器,应当返回414状态码。
 71 
 72 415    对于当前请求的方法和所请求的资源,请求中提交的实体并不是服务器中所支持的格式,因此请求被拒绝。
 73 
 74 416    如果请求中包含了 Range 请求头,并且 Range 中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义 If-Range 请求头,那么服务器就应当返回416状态码。   假如 Range 使用的是字节范围,那么这种情况就是指请求指定的所有数据范围的首字节位置都超过了当前资源的长度。服务器也应当在返回416状态码的同时,包含一个 Content-Range 实体头,用以指明当前资源的长度。这个响应也被禁止使用 multipart/byteranges 作为其 Content-Type。
 75 
 76 417    在请求头 Expect 中指定的预期内容无法被服务器满足,或者这个服务器是一个代理服务器,它有明显的证据证明在当前路由的下一个节点上,Expect 的内容无法被满足。
 77 
 78 421    从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址(比如用户的网关或者代理服务器地址)。在这种情况下,连接数的计算可能涉及到不止一个终端用户。
 79 
 80 422    从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址(比如用户的网关或者代理服务器地址)。在这种情况下,连接数的计算可能涉及到不止一个终端用户。
 81 422    请求格式正确,但是由于含有语义错误,无法响应。(RFC 4918 WebDAV)423 Locked   当前资源被锁定。(RFC 4918 WebDAV)
 82 
 83 424    由于之前的某个请求发生的错误,导致当前请求失败,例如 PROPPATCH。(RFC 4918 WebDAV)
 84 
 85 425    在WebDav Advanced Collections 草案中定义,但是未出现在《WebDAV 顺序集协议》(RFC 3658)中。
 86 
 87 426    客户端应当切换到TLS/1.0。(RFC 2817 88 
 89 449    由微软扩展,代表请求应当在执行完适当的操作后进行重试。
 90 
 91 500    服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。
 92 
 93 501    服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。
 94 
 95 502    作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
 96 
 97 503    由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个 Retry-After 头用以标明这个延迟时间。如果没有给出这个 Retry-After 信息,那么客户端应当以处理500响应的方式处理它。   注意:503状态码的存在并不意味着服务器在过载的时候必须使用它。某些服务器只不过是希望拒绝客户端的连接。
 98 
 99 504    作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。   注意:某些代理服务器在DNS查询超时时会返回400或者500错误
100 
101 505    服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本。这暗示着服务器不能或不愿使用与客户端相同的版本。响应中应当包含一个描述了为何版本不被支持以及服务器支持哪些协议的实体。
102 
103 506    由《透明内容协商协议》(RFC 2295)扩展,代表服务器存在内部配置错误:被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点。
104 
105 507    服务器无法存储完成请求所必须的内容。这个状况被认为是临时的。WebDAV (RFC 4918)
106 
107 509    服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用。
108 
109 510    获取资源所需要的策略并没有没满足。(RFC 2774


2.响应头

响应头通常也是由 字段名:值  组成 

响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。

常见的响应头字段:

Location:         这个头配合302状态码使用,用于告诉客户找谁。
Server:          服务器通过这个头告诉浏览器服务器的类型。
Content-Encoding: 服务器通过这个头告诉浏览器数据的压缩格式。
Content-Length:  服务器通过这个头告诉浏览器回送数据的长度
Content-Type:     服务器通过这个头告诉浏览器回送数据的类型
Last-Modified:     告诉浏览器当前资源的最后缓存时间
Refresh:          告诉浏览器隔多久刷新一次
Content-Disposition:告诉浏览器以下载方式打开数据
Transfer-Encoding:  告诉浏览器数据的传送格式
ETag:              缓存相关的头


其中三种禁止浏览器缓存的头字段:
 Expires:告诉浏览器把回送的资源缓存多长时间 -1或0则是不缓存
 Cache-Control:no-cache
 Pragma:no-cache
服务器通过以上两个头,也就是控制浏览器不要缓存数据

比如通过百度的GET某张图片--其相应响应头




1 Cache-Control    max-age=315360000
2 Connection    keep-alive
3 Date    Fri, 20 Mar 2015 06:05:22 GMT
4 Etag    "2c1-4a6473f6030c0"
5 Expires    Mon, 17 Mar 2025 06:05:23 GMT
6 Server    bfe/1.0.5.38
7 Set-Cookie    __bsi=16929409652610129136_00_122_R_N_1_0303_C02F_N_I_I; expires=Fri, 20-Mar-15 06:05:27 GMT; domain=www.baidu.com; path=/

 

3.响应体

响应体就是响应的消息体

如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,如此之类。

一个稍微完整的HTTP响应报文:


 

 

 

Http定义了与服务器交互的不同方法,最基本的方法有4种:GET、POST、PUT、DELETE

而HTTP中的GET,POST,PUT,DELETE就对应着对URL资源的查,改,增,删4个操作。

所以说:GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。

 

主要区分一下get和post

1.提交数据的形式:

GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),会直接展现在地址栏中,以?分割URL和传输数据,参数之间以&相连

如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5 %A5%BD。

如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密,

得出如:%E4 %BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII。

而POST方法则会把数据放到请求数据字段中以&分隔各个字段,请求行不包含数据参数,地址栏也不会额外附带参数

 

2.提交数据的大小

get方法提交数据的大小直接影响到了URL的长度,但HTTP协议规范中其实是没有对URL限制长度的,

限制URL长度的是客户端或服务器的支持的不同所影响:

比如IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系统的支持。

post方式HTTP协议规范中也没有限定,起限制作用的是服务器的处理程序的处理能力。

所以大小的限制还是得受各个web服务器配置的不同而影响着。

 

3.提交数据的安全

POST比GET方式的安全性要高

通过GET提交数据,用户名和密码将明文出现在URL上,因为一下几个原因get方式安全性会比post弱:

(1)登录页面有可能被浏览器缓存

(2)其他人查看浏览器的历史纪录,那么别人就可 以拿到你的账号和密码了

(3)当遇上跨站的攻击时,安全性的表现更差了

4.提交数据的安全(1)

tip:这里的安全,不是上诉所述的安全。而是只数据是否会被修改,完整性是否会被破坏的意思。

"  在ASP中,服务端获取GET请求参数用Request.QueryString,获取POST请求参数用Request.Form。

在JSP中,用 request.getParameter(\"XXXX\")来获取,虽然jsp中也有request.getQueryString()方法,但使用 起来比较麻烦

比如:传一个test.jsp?name=hyddd&password=hyddd,

用 request.getQueryString()得到的是:name=hyddd&password=hyddd。

在PHP中,可以 用$_GET和$_POST分别获取GET和POST中的数据,而$_REQUEST则可以获取GET和POST两种请求中的数据。

值得注意的是,JSP 中使用request和PHP中使用$_REQUEST都会有隐患  "


作者:u012938194 发表于2016/7/22 22:27:04 原文链接
阅读:40 评论:0 查看评论

MySQL原来也有内存数据库

$
0
0

以前只知道MySQL可以做分库分表,支持多种数据库引擎。这次听了来自台湾的MySQL专家Ivan Tu的讲座,有两点印象深刻,一是MySQL具有丰富的高可用方案,二是MySQL也有内存数据库,即MySQL Cluster CGE。Ivan形象的将内存数据库比喻成浅浅的盘子,而传统的MySQL磁盘存储方式比喻成深深的瓶子。

这里写图片描述

另外,也探讨了MySQL作为开源数据库,和Oracle数据库的关系。MySQL并行发展企业版和社区版,企业版增加了很多企业级的特性,如安全,备份和恢复,监控,以及CGE(电信运营商版)。MySQL目前主要的目的还是保持生态系统,培养群众基础,营收主要靠服务,培训等。毕竟开源不代表免费。

这里写图片描述

不过好像MySQL的内存数据库特性不是那么知名,找个时间研究一下吧。

作者:stevensxiao 发表于2016/7/22 22:45:12 原文链接
阅读:58 评论:0 查看评论

HDU 1026 Ignatius and the Princess I 【优先队列+路径输出】

$
0
0

Ignatius and the Princess I

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 16879    Accepted Submission(s): 5395
Special Judge


Problem Description
The Princess has been abducted by the BEelzebub feng5166, our hero Ignatius has to rescue our pretty Princess. Now he gets into feng5166's castle. The castle is a large labyrinth. To make the problem simply, we assume the labyrinth is a N*M two-dimensional array which left-top corner is (0,0) and right-bottom corner is (N-1,M-1). Ignatius enters at (0,0), and the door to feng5166's room is at (N-1,M-1), that is our target. There are some monsters in the castle, if Ignatius meet them, he has to kill them. Here is some rules:

1.Ignatius can only move in four directions(up, down, left, right), one step per second. A step is defined as follow: if current position is (x,y), after a step, Ignatius can only stand on (x-1,y), (x+1,y), (x,y-1) or (x,y+1).
2.The array is marked with some characters and numbers. We define them like this:
. : The place where Ignatius can walk on.
X : The place is a trap, Ignatius should not walk on it.
n : Here is a monster with n HP(1<=n<=9), if Ignatius walk on it, it takes him n seconds to kill the monster.

Your task is to give out the path which costs minimum seconds for Ignatius to reach target position. You may assume that the start position and the target position will never be a trap, and there will never be a monster at the start position.
 

Input
The input contains several test cases. Each test case starts with a line contains two numbers N and M(2<=N<=100,2<=M<=100) which indicate the size of the labyrinth. Then a N*M two-dimensional array follows, which describe the whole labyrinth. The input is terminated by the end of file. More details in the Sample Input.
 

Output
For each test case, you should output "God please help our poor hero." if Ignatius can't reach the target position, or you should output "It takes n seconds to reach the target position, let me show you the way."(n is the minimum seconds), and tell our hero the whole path. Output a line contains "FINISH" after each test case. If there are more than one path, any one is OK in this problem. More details in the Sample Output.
 

Sample Input
5 6 .XX.1. ..X.2. 2...X. ...XX. XXXXX. 5 6 .XX.1. ..X.2. 2...X. ...XX. XXXXX1 5 6 .XX... ..XX1. 2...X. ...XX. XXXXX.
 

Sample Output
It takes 13 seconds to reach the target position, let me show you the way. 1s:(0,0)->(1,0) 2s:(1,0)->(1,1) 3s:(1,1)->(2,1) 4s:(2,1)->(2,2) 5s:(2,2)->(2,3) 6s:(2,3)->(1,3) 7s:(1,3)->(1,4) 8s:FIGHT AT (1,4) 9s:FIGHT AT (1,4) 10s:(1,4)->(1,5) 11s:(1,5)->(2,5) 12s:(2,5)->(3,5) 13s:(3,5)->(4,5) FINISH It takes 14 seconds to reach the target position, let me show you the way. 1s:(0,0)->(1,0) 2s:(1,0)->(1,1) 3s:(1,1)->(2,1) 4s:(2,1)->(2,2) 5s:(2,2)->(2,3) 6s:(2,3)->(1,3) 7s:(1,3)->(1,4) 8s:FIGHT AT (1,4) 9s:FIGHT AT (1,4) 10s:(1,4)->(1,5) 11s:(1,5)->(2,5) 12s:(2,5)->(3,5) 13s:(3,5)->(4,5) 14s:FIGHT AT (4,5) FINISH God please help our poor hero. FINISH
 

Author
Ignatius.L

题意:一个人从左上角出发,到达有下角,‘.’是路,‘X’是墙,数字代表怪物,遇到怪物要把它消灭才能通过,消灭怪兽的时间等于其数值。很明显的要用优先队列+BFS,但记录路径有点困难。

路径怎么记录呢?先举一个简单的例子,假设路径只是一个点,我们用a[]表示,其值与下标的关系:
若a[x]=y,代表到达x这个点的上一点的坐标是y。
此题要用一个结构体的数组,每次BFS的时候,如果下一步可以走,便把当前坐标(上一步)记录下来。
输出的时候倒着输出就可以了。

AC代码:

#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#include <stack>
using namespace std;
const int maxn=105;
char a[maxn][maxn];
bool vis[maxn][maxn];
int n,m,ans;
int dir[4][2]= {0,1,1,0,0,-1,-1,0};
bool flag;

struct node
{
    int x,y,t;
    friend bool operator<(node a, node b)
    {
        return a.t>b.t;
    }
};
node pre[maxn][maxn];
bool OK(node no)
{
    if(no.x<0||no.x>=n||no.y<0||no.y>=m)
        return false;
    return true;
}
void BFS()
{
    memset(vis,false,sizeof(vis));
    vis[0][0]=true;
    priority_queue<node>q;
    node now,next;
    now.x=0;
    now.y=0;
    now.t=0;
    q.push(now);
    while(!q.empty())
    {
        now=q.top();
        q.pop();
        if(now.x==n-1&&now.y==m-1)
        {
            ans=now.t;
            flag=true;
            break;
        }
        for(int i=0; i<4; i++)
        {
            next.x=now.x+dir[i][0];
            next.y=now.y+dir[i][1];
            if(OK(next)&&!vis[next.x][next.y]&&a[next.x][next.y]!='X')
            {
                vis[next.x][next.y]=true;
                if(a[next.x][next.y]=='.')
                    next.t=now.t+1;
                else
                    next.t=now.t+(a[next.x][next.y]-'0')+1;
                q.push(next);

                pre[next.x][next.y].x=now.x;
                pre[next.x][next.y].y=now.y;
                pre[next.x][next.y].t=now.t;
            }
        }
    }
}
void show()
{
    stack<node> s;
    node now;
    now=pre[n-1][m-1];

    now.x=n-1;
    now.y=m-1;
    //s.push(now);
    while(now.x!=0||now.y!=0)
    {
        s.push(now);
        now=pre[now.x][now.y];
    }
    int t=1;
    while(!s.empty())
    {
        now=s.top();
        s.pop();
        if(a[now.x][now.y]=='.')
        {
            printf("%ds:(%d,%d)->(%d,%d)\n",t++,pre[now.x][now.y].x,pre[now.x][now.y].y,now.x,now.y);
        }
        else
        {
            printf("%ds:(%d,%d)->(%d,%d)\n",t++,pre[now.x][now.y].x,pre[now.x][now.y].y,now.x,now.y);
            int k=a[now.x][now.y]-'0';
            while(k--)
                printf("%ds:FIGHT AT (%d,%d)\n",t++,now.x,now.y);
        }
    }
}
int main()
{
    while(cin>>n>>m)
    {
        for(int i=0; i<n; i++)
            cin>>a[i];
        ans=0;
        flag=false;
        pre[0][0].x=pre[0][0].y=0;
        BFS();
        if(flag)
        {
            printf("It takes %d seconds to reach the target position, let me show you the way.\n",ans);
            show();
        }
        else
        {
            printf("God please help our poor hero.\n");
        }
        cout<<"FINISH"<<endl;

    }
    return 0;
}


作者:hurmishine 发表于2016/7/22 22:47:16 原文链接
阅读:57 评论:0 查看评论

Android——ListView控件(Android Studio)

$
0
0

本篇介绍ListView控件,这是Android中比较重要也比较复杂的控件,这里只谈到使用ViewHolder机制优化即可。

一、ListView简介

ListView是Android系统中显示列表的控件,每个ListView都可以包含很多个列表项。
这里写图片描述

二、ListView的使用

概念不多说,直接来介绍使用方法。
ListView中比较复杂的是数据适配器,其作用是把复杂的数据(数组、链表、数据库、集合等)填充在指定视图界面,是连接数据源和视图界面的桥梁。常见的Android原生的适配器有ArrayAdapter和SimpleAdapter。
使用步骤:新建适配器->添加数据源到适配器->视图加载适配器

1. ArrayAdapter(数组适配器)

适用:用于绑定格式单一的数据;
数据源:可以使集合或数组。

public class MainActivity extends Activity {
    private ListView listView;
// 1. 新建一个数据适配器
    private ArrayAdapter<String> arr_aAdapter; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView = (ListView)findViewById(R.id.listView1);
        //  创建适配器对象时将数据加载到适配器里
   /**new ArrayAdapter<T>(context, textViewResourceId)
         * context--  上下文,一般为this
         * textViewResourceId-- 当前ListView加载的每一个列表项所对应的布局文件【这里采用系统默认的一个布局android.R.layout.simple_list_item_1】
      */
      //  2. 添加数据源到适配器
        String[] arr_data = {"fanff", "fan", "tencent", "QQ"};// 创建的数据源
        arr_aAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arr_data);
        // 3. 视图(ListView)加载适配器
        listView.setAdapter(arr_adAdapter);
    }    
}

2. SimpleAdapter(简单适配器)

适用:绑定格式复杂的数组;
数据源:只能是特定泛型的集合。

public class MainActivity extends Activity {
    private ListView listView;
    private SimpleAdapter sim_aAdapter; // 1. 新建一个数据适配器

    private List<Map<String, Object>>dataList; // 数据源

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        listView = (ListView)findViewById(R.id.listView1); 

        /** SimpleAdapter(context, data, resource, from, to)
         *  context: 上下文
         *  data: 数据源(List<? extends Map<String, ?>> data),一个Map所组成的List集合
         *        每个Map都会对应ListView列表中的一行,Map是由键【必须包含所有在from中所指定的键】值对组成
         *  resource:列表中的布局文件的ID,此处的布局是自定义的
         *  from:Map中的键名
         *  to:绑定数据视图中的ID,与from成对应关系
         */  
        // 2. 适配器加载数据源
        dataList = new ArrayList<Map<String, Object>>();
        sim_aAdapter = new SimpleAdapter(this, getData(), R.layout.item, new String[]{"pic0", "text0"}, new int[]{R.id.pic, R.id.text}); 
        // 3. 视图(ListView)加载适配器
        listView.setAdapter(sim_aAdapter);        
    }

   private List<Map<String, Object>> getData(){
       for (int i = 0; i < 20; i++){
           Map<String, Object>map = new HashMap<String, Object>();
           map.put("pic0", R.drawable.ic_launcher);
           map.put("text0", "fanff"+i);
           dataList.add(map);
       }
       return dataList;
   }   
}

一般来讲,简单适配器的数据源是一个集合,所以一般写一个方法来处理(例如getData())。

其中自定义的item.xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/pic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:src="@drawable/ic_launcher"
        />

    <TextView
        android:id="@+id/text"
        android:layout_marginTop="12dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#000000"
        android:text="hello" 
        />
</LinearLayout>

3.继承BaseAdapter的自定义的适配器

这个玩法比较多,这里先不介绍,直接见下面的。

4. 监听器

(1). 监听器是程序和用户(或系统)交互的桥梁,这里不多讲了,毕竟用的多。ListView中的两个常用监听器:OnItemClickListener和OnScrollListener。
(2). OnItemClickListener可以处理视图中单个条目的点击事件;OnScrollListener监测滚动的变化,可以用于视图在滚动中加载数据。

public class MainActivity extends Activity implements OnItemClickListener,
        OnScrollListener {
    private ListView listView;
    private ArrayAdapter<String> arr_aAdapter;
    private SimpleAdapter sim_aAdapter;

    private List<Map<String, Object>> dataList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        listView = (ListView) findViewById(R.id.listView1);

        /**
         * 1. 新建一个数据适配器 context: 上下文 data: 数据源(List<? extends Map<String, ?>>
         * data),一个Map所组成的List集合
         * 每个Map都会对应ListView列表中的一行,Map是由键【必须包含所有在from中所指定的键】值对组成 from:Map中的键名
         * resource:列表中的布局文件的ID to:绑定数据视图中的ID,与from成对应关系
         */
        // 2. 适配器加载数据源
        dataList = new ArrayList<Map<String, Object>>();
        sim_aAdapter = new SimpleAdapter(this, getData(), R.layout.item,
                new String[] { "pic0", "text0" }, new int[] { R.id.pic,
                        R.id.text });
        // 3. 视图(ListView)加载适配器
        listView.setAdapter(sim_aAdapter);

        // 监听器
        listView.setOnItemClickListener(this);// 单击单个条目
        listView.setOnScrollListener(this);// 视图在滚动中加载数据
    }

    private List<Map<String, Object>> getData() {
        for (int i = 0; i < 20; i++) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("pic0", R.drawable.ic_launcher);
            map.put("text0", "fanff" + i);
            dataList.add(map);
        }
        return dataList;
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // TODO Auto-generated method stub
        switch (scrollState) {
        case SCROLL_STATE_FLING:
            Log.i("ScrollState", "用户在手指离开屏幕之前,由于用力滑了一下,视图仍依靠惯性在继续滑行");
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("pic0", R.drawable.ic_launcher);
            map.put("text0", "fresh");
            dataList.add(map);
            sim_aAdapter.notifyDataSetChanged();// 通知UI进程刷新界面
            break;
        case SCROLL_STATE_IDLE:
            Log.i("ScrollState", "视图已经停止滑动");
            break;
        case SCROLL_STATE_TOUCH_SCROLL:
            Log.i("ScrollState", "手指乜有离开屏幕,视图正在滑动");
            break;
        default:
            break;
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position,
            long id) {
        // TODO Auto-generated method stub
        String text = listView.getItemAtPosition(position) + "";// 指定位置的内容
        Toast.makeText(this, "positon=" + position + "text" + text,
                Toast.LENGTH_LONG).show();
    }
}

三、ListView中的BaseAdapter

这里着重来介绍一下BaseAdapter,各种方式的比较见代码注释。
源码下载:https://github.com/herdyouth/ListView
MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<ItemBean> itemBeanList = new ArrayList<>();
        for (int i = 0; i < 20; i++){
            itemBeanList.add(new ItemBean(R.mipmap.ic_launcher,
                    "标题" + i, "内容" + i));
        }

        // 数据源与适配器的绑定
        ListView listView = (ListView) findViewById(R.id.lview);
        listView.setAdapter(new MyBaseAdapter(this, itemBeanList));
    }
}

BaseAdapter各种方式的对比,一步步优化的原因如代码注释

/**
 * 创建数据适配器
 * Created by herd_youth on 2016/4/15.
 */
public class MyBaseAdapter extends BaseAdapter{

    private List<ItemBean> mList;
    private LayoutInflater mInflater;

    // 通过构造器关联数据源与数据适配器
    public MyBaseAdapter(Context context, List<ItemBean> list){
        mList = list;
        // 使用当前要使用的界面对象context去初始化布局装载器对象mInflater
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        return mList.get(position);
    }

    // 返回指定索引对应的数据项
    @Override
    public long getItemId(int position) {
        return position;
    }


   /* *//**
     * 返回每一项对应的内容
     * 缺点:没有利用到ListView的缓存机制
     *      每次都会创建新的View,不管当前这个Item是否在屏幕上被调用过(即是否被缓存过)
     * @param position
     * @param convertView
     * @param parent
     * @return
     *//*
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 将布局文件转为View对象
        View view = mInflater.inflate(R.layout.item, null);
        ImageView imageView = (ImageView) view.findViewById(R.id.iv_img);
        TextView title = (TextView) view.findViewById(R.id.tv_title);
        TextView content = (TextView) view.findViewById(R.id.tv_content);
        ItemBean bean = mList.get(position);
        imageView.setImageResource(bean.getItemImageResid());
        title.setText(bean.getItemContent());
        content.setText(bean.getItemContent());

        return view;
    }*/

    /**
     * 改善处:使用系统的convertView来较好的利用ListView的缓存机制,避免重复大量的创建convertView
     * 缺点:findViewById依然会浪费大量的时间去调用视图树
     * @param position
     * @param convertView
     * @param parent
     * @return
     *//*
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null){// View未被实例化,即缓冲池中无缓存才创建View
            convertView = mInflater.inflate(R.layout.item, null);
        }else{
            ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_img);
            TextView title = (TextView) convertView.findViewById(R.id.tv_title);
            TextView content = (TextView) convertView.findViewById(R.id.tv_content);
            ItemBean bean = mList.get(position);
            imageView.setImageResource(bean.getItemImageResid());
            title.setText(bean.getItemContent());
            content.setText(bean.getItemContent());
        }
        return convertView;
    }*/

    /**
     * 既利用了ListView的缓存,
     * 更通过ViewHolder类来显示数据的视图的缓存,避免了多次通过findViewById寻找控件
     * @param position
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null){// View未被实例化,即缓冲池中无缓存才创建View
            // 将控件id保存在viewHolder中
            viewHolder = new ViewHolder();
            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.iv_img);
            viewHolder.title = (TextView) convertView.findViewById(R.id.tv_title);
            viewHolder.content = (TextView) convertView.findViewById(R.id.tv_content);
            // 通过setTag将ViewHolder与convertView绑定
            convertView.setTag(viewHolder);
        } else{
            // 通过ViewHolder对象找到对应控件
            viewHolder = (ViewHolder) convertView.getTag();
            ItemBean bean = mList.get(position);
            viewHolder.imageView.setImageResource(bean.getItemImageResid());
            viewHolder.title.setText(bean.getItemContent());
            viewHolder.content.setText(bean.getItemContent());
        }
        return convertView;
    }

    // 避免重复的findViewById的操作
    class ViewHolder{
        public ImageView imageView;
        public TextView title;
        public TextView content;
    }
}
/**
 * 创建设置每个Item的类
 * Created by herd_youth on 2016/4/15.
 */
public class ItemBean {
    private int ItemImageResid;
    private String ItemTitle;
    private String ItemContent;

    public ItemBean(int itemImageResid, String itemTitle, String itemContent) {
        ItemImageResid = itemImageResid;
        ItemTitle = itemTitle;
        ItemContent = itemContent;
    }

    public int getItemImageResid() {
        return ItemImageResid;
    }

    public void setItemImageResid(int itemImageResid) {
        ItemImageResid = itemImageResid;
    }

    public String getItemTitle() {
        return ItemTitle;
    }

    public void setItemTitle(String itemTitle) {
        ItemTitle = itemTitle;
    }

    public String getItemContent() {
        return ItemContent;
    }

    public void setItemContent(String itemContent) {
        ItemContent = itemContent;
    }
}

======无聊分割线=======
ListView是个比较难得玩的控件,这里我只是谈了我现在知道的一点,希望玩的更好的人继续添加。

作者:u014294166 发表于2016/7/22 22:48:07 原文链接
阅读:42 评论:0 查看评论

C++11 中的右值引用与转移语义

$
0
0

本文介绍了 C++11 标准中的一个特性,右值引用和转移语义。这个特性能够使代码更加简洁高效。

新特性的目的

右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move Sementics) 和精确传递 (Perfect Forwarding)。它的主要目的有两个方面:

  • 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
  • 能够更简洁明确地定义泛型函数。

左值与右值的定义

C++(包括 C) 中所有的表达式和变量要么是左值,要么是右值。通俗的左值的定义就是非临时对象,那些可以在多条语句中使用的对象。所有的变量都满足这个定义,在多条代码中都可以使用,都是左值。右值是指临时的对象,它们只在当前的语句中有效。请看下列示例 :

  • 简单的赋值语句
int i = 0;

在这条语句中,i 是左值,0 是临时值,就是右值。在下面的代码中,i 可以被引用,0 就不可以了。立即数都是右值。

  • 右值也可以出现在赋值表达式的左边,但是不能作为赋值的对象,因为右值只在当前语句有效,赋值没有意义。如:((i>0) ? i : j) = 1;在这个例子中,0 作为右值出现在了”=” 的左边。但是赋值对象是 i 或者 j,都是左值。在 C++11 之前,右值是不能被引用的,最大限度就是用常量引用绑定一个右值,如 :
const int &a = 1;

在这种情况下,右值不能被修改的。但是实际上右值是可以被修改的,如 :

T().set().get();

T 是一个类,set 是一个函数为 T 中的一个变量赋值,get 用来取出这个变量的值。在这句中,T() 生成一个临时对象,就是右值,set() 修改了变量的值,也就修改了这个右值。

既然右值可以被修改,那么就可以实现右值引用。右值引用能够方便地解决实际工程中的问题,实现非常有吸引力的解决方案。

左值和右值的语法符号

左值的声明符号为”&”, 为了和左值区分,右值的声明符号为”&&”。

示例程序 :

void process_value(int& i) 
{ 
    std::cout << "LValue processed:" << i << std::endl; 
} 

void process_value(int&& i) 
{ 
    std::cout << "RValue processed:" << i << std::endl; 
} 

int main() 
{ 
    int a = 0; 
    process_value(a); 
    process_value(1); 
}

运行结果 :

 LValue processed: 0 
 RValue processed: 1

Process_value 函数被重载,分别接受左值和右值。由输出结果可以看出,临时对象是作为右值处理的。

但是如果临时对象通过一个接受右值的函数传递给另一个函数时,就会变成左值,因为这个临时对象在传递过程中,变成了命名对象。

示例程序 :

void process_value(int& i) 
{ 
    std::cout << "LValue processed:" << i << std::endl; 
} 

void process_value(int&& i) 
{ 
    std::cout << "RValue processed:" << i << std::endl; 
} 

void forward_value(int&& i) 
{ 
    process_value(i); 
} 

int main() 
{ 
    int a = 0; 
    process_value(a); 
    process_value(1); 
    forward_value(2); 
}

运行结果 :

LValue processed: 0 
RValue processed: 1 
LValue processed: 2

虽然 2 这个立即数在函数 forward_value 接收时是右值,但到了 process_value 接收时,变成了左值。

转移语义的定义

右值引用是用来支持转移语义的。转移语义可以将资源 (堆,系统对象等) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。临时对象的维护 ( 创建和销毁 ) 对性能有严重影响。

转移语义是和拷贝语义相对的,可以类比文件的剪切与拷贝,当我们将文件从一个目录拷贝到另一个目录时,速度比剪切慢很多。

通过转移语义,临时对象中的资源能够转移其它的对象里。

在现有的 C++ 机制中,我们可以定义拷贝构造函数和赋值函数。要实现转移语义,需要定义转移构造函数,还可以定义转移赋值操作符。对于右值的拷贝和赋值会调用转移构造函数和转移赋值操作符。如果转移构造函数和转移拷贝操作符没有定义,那么就遵循现有的机制,拷贝构造函数和赋值操作符会被调用。

普通的函数和操作符也可以利用右值引用操作符实现转移语义。

实现转移构造函数和转移赋值函数

以一个简单的 string 类为示例,实现拷贝构造函数和拷贝赋值操作符。

示例程序 :

class MyString 
{ 
private: 
    char* _data; 
    size_t   _len; 
    void _init_data(const char *s) 
    { 
        _data = new char[_len+1]; 
        memcpy(_data, s, _len); 
        _data[_len] = '\0'; 
    } 

public: 
    MyString() 
    { 
        _data = NULL; 
        _len = 0; 
    } 

    MyString(const char* p) 
    { 
        _len = strlen (p); 
        _init_data(p); 
    } 

    MyString(const MyString& str) 
    { 
        _len = str._len; 
        _init_data(str._data); 
        std::cout << "Copy Constructor is called! source:" << str._data << std::endl; 
    } 

    MyString& operator=(const MyString& str) 
    { 
        if (this != &str) { 
            _len = str._len; 
            _init_data(str._data); 
        } 
        std::cout << "Copy Assignment is called! source:" << str._data << std::endl; 
        return *this; 
    } 

    virtual ~MyString() 
    { 
        if (_data) 
            free(_data); 
    } 
}; 

int main() 
{ 
    MyString a; 
    a = MyString("Hello"); 
    std::vector<MyString> vec; 
    vec.push_back(MyString("World")); 
}

运行结果 :

Copy Assignment is called! source: Hello 
Copy Constructor is called! source: World

这个 string 类已经基本满足我们演示的需要。在 main 函数中,实现了调用拷贝构造函数的操作和拷贝赋值操作符的操作。

MyString(“Hello”) 和 MyString(“World”) 都是临时对象,也就是右值。虽然它们是临时的,但程序仍然调用了拷贝构造和拷贝赋值,造成了没有意义的资源申请和释放的操作。如果能够直接使用临时对象已经申请的资源,既能节省资源,有能节省资源申请和释放的时间。这正是定义转移语义的目的。

我们先定义转移构造函数。

MyString(MyString&& str) 
{ 
    std::cout << "Move Constructor is called! source:" << str._data << std::endl; 
    _len = str._len; 
    _data = str._data; 
    str._len = 0; 
    str._data = NULL; 
}

和拷贝构造函数类似,有几点需要注意:

  1. 参数(右值)的符号必须是右值引用符号,即 “&&”。

  2. 参数(右值)不可以是常量,因为我们需要修改右值。

  3. 参数(右值)的资源链接和标记必须修改。否则,右值的析构函数就会释放资源。转移到新对象的资源也就无效了。

现在我们定义转移赋值操作符。

MyString& operator=(MyString&& str) 
{ 
    std::cout << "Move Assignment is called! source:" << str._data << std::endl; 
    if (this != &str) { 
      _len = str._len; 
      _data = str._data; 
      str._len = 0; 
      str._data = NULL; 
    } 
    return *this; 
}

这里需要注意的问题和转移构造函数是一样的。

增加了转移构造函数和转移复制操作符后,我们的程序运行结果为 :

Move Assignment is called! source: Hello 
Move Constructor is called! source: World

由此看出,编译器区分了左值和右值,对右值调用了转移构造函数和转移赋值操作符。节省了资源,提高了程序运行的效率。

有了右值引用和转移语义,我们在设计和实现类时,对于需要动态申请大量资源的类,应该设计转移构造函数和转移赋值函数,以提高应用程序的效率。

标准库函数 std::move

既然编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,也就是把一个左值引用当做右值引用来使用,怎么做呢?标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用。

示例程序 :

void ProcessValue(int& i) 
{ 
    std::cout << "LValue processed:" << i << std::endl; 
} 

void ProcessValue(int&& i) 
{ 
    std::cout << "RValue processed:" << i << std::endl; 
} 

int main() 
{ 
    int a = 0; 
    ProcessValue(a); 
    ProcessValue(std::move(a)); 
}

运行结果 :

LValue processed: 0 
RValue processed: 0

std::move在提高 swap 函数的的性能上非常有帮助,一般来说,swap函数的通用定义如下:

template  swap(T& a, T& b) 
{ 
    T tmp(a);   // copy a to tmp 
    a = b;      // copy b to a 
    b = tmp;    // copy tmp to b 
}

有了 std::move,swap 函数的定义变为 :

template  swap(T& a, T& b) 
{ 
    T tmp(std::move(a)); // move a to tmp 
    a = std::move(b);    // move b to a 
    b = std::move(tmp);  // move tmp to b 
}

通过 std::move,一个简单的 swap 函数就避免了 3 次不必要的拷贝操作。

精确传递 (Perfect Forwarding)

本文采用精确传递表达这个意思。”Perfect Forwarding” 也被翻译成完美转发,精准转发等,说的都是一个意思。

精确传递适用于这样的场景:需要将一组参数原封不动的传递给另一个函数。

“原封不动” 不仅仅是参数的值不变,在 C++ 中,除了参数值之外,还有一下两组属性:

左值/右值和 const/non-const。 精确传递就是在参数传递过程中,所有这些属性和参数值都不能改变。在泛型函数中,这样的需求非常普遍。

下面举例说明。函数 forward_value 是一个泛型函数,它将一个参数传递给另一个函数 process_value。

forward_value 的定义为:

template <typename T> void forward_value(const T& val) 
{ 
    process_value(val); 
} 

template <typename T> void forward_value(T& val) 
{ 
    process_value(val); 
}

函数 forward_value 为每一个参数必须重载两种类型,T& 和 const T&,否则,下面四种不同类型参数的调用中就不能同时满足 :

int a = 0; 
const int &b = 1; 
forward_value(a); // int& 
forward_value(b); // const int& 
forward_value(2); // int&

对于一个参数就要重载两次,也就是函数重载的次数和参数的个数是一个正比的关系。这个函数的定义次数对于程序员来说,是非常低效的。我们看看右值引用如何帮助我们解决这个问题 :

template  void forward_value(T&& val) 
{ 
    process_value(val); 
}

只需要定义一次,接受一个右值引用的参数,就能够将所有的参数类型原封不动的传递给目标函数。四种不用类型参数的调用都能满足,参数的左右值属性和 const/non-cosnt 属性完全传递给目标函数 process_value。这个解决方案不是简洁优雅吗?

int a = 0; 
const int &b = 1; 
forward_value(a); // int& 
forward_value(b); // const int& 
forward_value(2); // int&&

C++11 中定义的 T&& 的推导规则为:

右值实参为右值引用,左值实参仍然为左值引用。

一句话,就是参数的属性不变。这样也就完美的实现了参数的完整传递。

右值引用,表面上看只是增加了一个引用符号,但它对 C++ 软件设计和类库的设计有非常大的影响。它既能简化代码,又能提高程序运行效率。每一个 C++ 软件设计师和程序员都应该理解并能够应用它。我们在设计类的时候如果有动态申请的资源,也应该设计转移构造函数和转移拷贝函数。在设计类库时,还应该考虑 std::move 的使用场景并积极使用它。

总结

右值引用和转移语义是 C++ 11 标准中的一个重要特性。每一个专业的 C++ 开发人员都应该掌握并应用到实际项目中。在有机会重构代码时,也应该思考是否可以应用新也行。在使用之前,需要检查一下编译器的支持情况。

作者:qq_20480611 发表于2016/7/22 22:52:37 原文链接
阅读:48 评论:0 查看评论

UVALive 5971 Permutation Counting 组合学、递推

$
0
0

Dexter considers a permutation of first N natural numbers good if it doesn’t have x and x+1 appearing consecutively, where (1 ≤ x < N). For example, for N = 3, all good permutations are:

1. {1, 3, 2}

2. {2, 1, 3}

3. {3, 2, 1}

Input

Input starts with an integer T (≤ 10000), denoting the number of test cases.Each case starts with a line containing an integer N (1 ≤ N ≤ 106).

Output

For each case, print the case number and the number of good permutations modulo 1000 000 007.


Sample Input

3

2

3

5

Sample Output

Case 1: 1

Case 2: 3

Case 3: 53


Source

UESTC 2016 Summer Training #11 Div.2

UVALive 5971


My Solution

反正枚举全排列必定TLE的, 然后排列组合里面其实递推挺多的, 也可以搞出前几项, 然后去 数列网站上查一下

这里的递推式是 dp[i] = dp[i - 1] * (i - 1) + dp[i - 2] * (i - 2)

这里dp[i] 表示 n == i 时合法的排列数

1)对于 dp[i - 1] * (i - 1) 则是对于 n == i - 1的合法排列, 可以在 (i - 1)个地方把第i个数插进去就好了, 总共 (i - 1 + 1) - 1个位置;

2)对于dp[i - 2] * (i - 2)则因为对于 n == i - 1的一些不合法序列, 插入一个i其实也可以变成dp[i]的一个合法序列, 

      比如对应dp[i - 1] 其中的不合法序列中有一个 ......1 2, 其它地方合法, 那么把i插入到 1 2 之间, 变成 ........ 1 i 2 就合法了, 

     那这样只有一个地方pair不合法的 n == i - 1 的个数刚好是 dp[i - 2] * (i - 2)]的个数, 也就是把那个不合法的pair看成一个数

     也就是对于dp[i - 2]的每一种情况, 取x 然后把x+1 用i - 1换出来, 然后把x + 1 放到x 后面, 然后把 i 放到x 和 x+1之间有 i- 2个位置可以这么做

     所以dp[i - 2] * (i - 2)]

复杂度 O(n) 预处理 T*O(1)查询


#include <iostream>
#include <cstdio>

using namespace std;
typedef long long LL;
const int maxn = 1e6 + 8;
const LL Hash = 1000000007;

LL dp[maxn];

inline LL mod(LL x)
{
    return x - x/Hash*Hash;
}

int main()
{
    #ifdef LOCAL
    freopen("a.txt", "r", stdin);
    //freopen("b.txt", "w", stdout);
    #endif // LOCAL
    dp[1] = 1; dp[2] = 1; dp[3] = 3;
    for(int i = 4; i < maxn; i++)
        dp[i] = mod( mod(dp[i - 1]*(i - 1)) + mod(dp[i - 2]*(i - 2)) );

    int T, n, kase = 0;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        printf("Case %d: %lld\n", ++kase, dp[n]);

    }
    return 0;
}

  Thank you!

                                                                                                                                               ------from ProLights

作者:ProLightsfxjh 发表于2016/7/22 22:57:14 原文链接
阅读:4 评论:0 查看评论

HDU1397 POJ2909 UVA686 Goldbach's Conjecture

$
0
0

问题链接:HDU1397 POJ2909 UVA686 Goldbach's Conjecture基础训练级的题,用C语言编写程序。

这个问题是验证哥德巴赫猜想,对于输入的n,计算和等于n的奇素数对的个数。

不预先打表时间上会超时。

AC的C语言程序如下:

/* HDU1397 POJ2909 UVA686 Goldbach's Conjecture */

#include <stdio.h>
#include <math.h>

#define MAXN 32767

int prime[MAXN+2] = {0, 0, 1, 3, 0};

// 试除法判断一个数是否为素数
int isprime(int n)
{
    int end2, i;

    end2 = sqrt(n);
    for(i=3; i<=end2; i+=2) {
        if(n % i == 0)
            break;
    }

    return i > end2 ? 1 : 0;
}

void maketable(int n)
{
    int i;

    for(i=5; i<n; i+=2) {
        prime[i] = isprime(i);
        prime[i+1] = 0;
    }
}

int main(void)
{
    int n;
    int count, i;

    // 打表
    maketable(MAXN);

    while(scanf("%d", &n) != EOF) {
        // 判定结束条件
        if(n == 0)
            break;

        // 计算素数对个数
        count = 0;
        for(i=3; i<=n/2; i+=2)
            if(prime[i] && prime[n-i])
                count++;

        // 输出结果
        printf("%d\n", count);
    }

    return 0;
}




作者:tigerisland45 发表于2016/7/22 23:11:06 原文链接
阅读:3 评论:0 查看评论

UVALive 5963 Confusion in the Problem Set 思维题、Interesting

$
0
0

A small confusion in a problem set may ruin the whole contest. So most of the problem setters trytheir best to remove any kind of ambiguity from the set. But sometimes it is not that important. Forexample the mock of last contest. As it was mock contest so we were not that serious with the set. Weprinted two problems, problem A in Page 1 and Problem B in Page 2. Then we remembered we haveto give rule of the contest too. So we printed the rule page. But we did not notice that the rule pagewas printed with Page 2. We were stapling 3 pages together. First rule page, then Problem A and atthe last Problem B. So page numbers were, 2, 1 and 2. This looked odd. But we already printed all thepages and if we want to fix the issue we had no other way but print all the three pages. One amongus suggested an explanation — “Well, first 2 means there are 2 pages after this page. 1 also meansthere is 1 page after this page. But the last 2 means there are 2 pages before this page.” Interestingobservation indeed. So we came up with a rule which is, page numberings of all the n pages are valid,if the page number at a page denotes number of pages before this page or number of page after thispage.

So with this rule, {3, 1, 2, 0} is valid but {3, 3, 1, 3} is not valid.

Input

First line of the input file contains number of tests cases T (< 60).

Then T test cases follow. First line of each test case contains a positive number n (≤ 104) numberof pages in the problem set. Then there are n space separated numbers in the following line each ofwhich ranges from 0 to 10^6.

Output

For each test case output the test case number. Then output ‘yes’ (without the quotes) if the pagescan be shuffled somehow so that the pages numbering is valid. Otherwise output ‘no’.

For the exact format of the output please follow the sample.

Explanation of the samples:

1. The pages can be shuffled in several ways so that the page numbering is valid. One of the validshuffles are 3, 1, 2, 0.

2. There is no valid way to shuffle these.


Sample Input

2

4

0 3 1 2

4

1 3 3 3


Sample Output

Case 1: yes

Case 2: no


source

UESTC 2016 Summer Training #11 Div.2

UVALive 5963


My Solution

把输入的数字全转化为0 到 (n -1)/2

cnt[min(val, n - 1 - val)]++;

并记录

则如果符合条件则每个数出现2次, 比如2个1, 2个2,... 

此外如果奇数则 则第 (n - 1)/2 + 1 也就是中间那个数对应的数字(n - 1)/2只出现一次, 如果偶数则 n/2 - 1 这个数出现2次。

复杂度 O(n)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 1e6 + 8;


int cnt[maxn], n;
bool flag;
/*果然TLE ⊙﹏⊙‖∣
void dfs(int k)
{
    if(k == n){
        flag = true;
        return;
    }
    if(flag) return;
    if(cnt[k] == 0 && cnt[n - 1 - k] == 0) return;
    if(cnt[k]) { cnt[k]--; dfs(k + 1); cnt[k]++;}
    if(cnt[n - 1 - k]) { cnt[n - 1 - k]--; dfs(k + 1); cnt[n - 1 - k]++;}

}
*/
int main()
{
    #ifdef LOCAL
    freopen("a.txt", "r", stdin);
    //freopen("b.txt", "w", stdout);
    #endif // LOCAL
    int T, val, kase = 0;
    scanf("%d", &T);
    while(T--){
        memset(cnt, 0, sizeof cnt);
        scanf("%d", &n);
        for(int i = 0; i < n; i++){
            scanf("%d", &val);
            cnt[min(val, n - 1 - val)]++;
        }
        flag = true;
        //dfs(0);

        for(int i = 0; i < (n - 1)/2; i++){
            if(cnt[i] != 2){flag = false; break; }
        }
        if(!flag) {printf("Case %d: no\n", ++kase); continue;}

        if(n&1 && cnt[(n - 1)/2] != 1) {printf("Case %d: no\n", ++kase); continue;}
        else if(!(n&1) && cnt[n/2 - 1] != 2) {printf("Case %d: no\n", ++kase); continue;}

        if(flag) printf("Case %d: yes\n", ++kase);
    }
    return 0;
}

   Thank you!

                                                                                                                                               ------from ProLights

作者:ProLightsfxjh 发表于2016/7/22 23:11:13 原文链接
阅读:0 评论:0 查看评论

Oracle到底有多少数据库?

$
0
0

Oracle到底有多少数据库? 这个问题还真没认真考虑过,我们来看一张Oracle数据管理解决方案图:

这里写图片描述

这个图是按适用企业规模大小从左至右排列的(也可以认为是footprint,即数据库的量级),旗舰产品当然是Oracle数据库,然后又内存数据库TimesTen和内存网格Coherence。MySQL 既有社区版,也有Cluster CGE 商用版。最右是适用于嵌入式和移动设备使用的Berkeley DB。

这其中:
* TimesTen是第一个商用的关系型数据库
* Oracle是排名第一的企业级数据库
* MySQL是排名第一的开源数据库
* Coherence在Forrest评测的内存网格中排名第一
* Berkeley DB是轻量级的非关系型数据库,使用方式非常灵活。象Amazon的购物车,EMC 的centera,Motorola智能手机,Google Accounts,还有很多POS系统都用了BDB。

这张图其实还不全,例如还有Oracle NoSQL数据库,不过Oracle NoSQL也是基于BDB开发的。还有Oracle Rdb,只能用在OpenVMS。
基本上应该列全了。

在来看看第三方的数据库排名

这里写图片描述

作者:stevensxiao 发表于2016/7/22 23:11:15 原文链接
阅读:0 评论:0 查看评论

【Leetcode】Wiggle Subsequence

$
0
0

题目链接:https://leetcode.com/problems/wiggle-subsequence/

题目:
A sequence of numbers is called a wiggle sequence if the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.

For example, [1,7,4,9,2,5] is a wiggle sequence because the differences (6,-3,5,-7,3) are alternately positive and negative. In contrast, [1,4,7,2,5] and [1,7,4,5,5] are not wiggle sequences, the first because its first two differences are positive and the second because its last difference is zero.

Given a sequence of integers, return the length of the longest subsequence that is a wiggle sequence. A subsequence is obtained by deleting some number of elements (eventually, also zero) from the original sequence, leaving the remaining elements in their original order.

Examples:
Input: [1,7,4,9,2,5]
Output: 6
The entire sequence is a wiggle sequence.

Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].

Input: [1,2,3,4,5,6,7,8,9]
Output: 2
Follow up:
Can you do it in O(n) time?

思路:
贪心。。easy,要注意相等情况,当相等时,要用一个标记记录前面是递增的,还是递减的。

算法:

    public int wiggleMaxLength(int[] nums) {
        Boolean flag = null;//true为递增
        if (nums.length <= 1)
            return nums.length;
        int count = 1;

        for (int i = 1; i < nums.length; i++) {
            if(nums[i]>nums[i-1]){//递增
                if(flag==null){//前面全相等
                    count++;
                    flag = true;
                }else{//前面位递减时
                    if(flag==false){
                        count++;
                        flag = true;
                    }
                }
            }else if(nums[i]<nums[i-1]){//与上同理
                if(flag==null){
                    count++;
                    flag = false;
                }else{
                    if(flag==true){
                        count++;
                        flag = false;
                    }
                }
            }
        }
        return count;
    }
作者:yeqiuzs 发表于2016/7/22 23:19:16 原文链接
阅读:0 评论:0 查看评论

Mysql 学习笔记

$
0
0
/* 启动MySQL */
net start mysql

/* 连接与断开服务器 */
mysql -h 地址 -P 端口 -u 用户名 -p 密码

/* 跳过权限验证登录MySQL */
mysqld --skip-grant-tables
-- 修改root密码
密码加密函数password()
update mysql.user set password=password('root');

SHOW PROCESSLIST -- 显示哪些线程正在运行
SHOW VARIABLES --

/* 数据库操作 */ ------------------
-- 查看当前数据库
    select database();
-- 显示当前时间、用户名、数据库版本
    select now(), user(), version();
-- 创建库
    create database[ if not exists] 数据库名 数据库选项
    数据库选项:
        CHARACTER SET charset_name
        COLLATE collation_name
-- 查看已有库
    show databases[ like 'pattern']
-- 查看当前库信息
    show create database 数据库名
-- 修改库的选项信息
    alter database 库名 选项信息
-- 删除库
    drop database[ if exists] 数据库名
        同时删除该数据库相关的目录及其目录内容

/* 表的操作 */ ------------------
-- 创建表
    create [temporary] table[ if not exists] [库名.]表名 ( 表的结构定义 )[ 表选项]
        每个字段必须有数据类型
        最后一个字段后不能有逗号
        temporary 临时表,会话结束时表自动消失
        对于字段的定义:
            字段名 数据类型 [NOT NULL | NULL] [DEFAULT default_value] [AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY] [COMMENT 'string']
-- 表选项
    -- 字符集
        CHARSET = charset_name
        如果表没有设定,则使用数据库字符集
    -- 存储引擎
        ENGINE = engine_name    
        表在管理数据时采用的不同的数据结构,结构不同会导致处理方式、提供的特性操作等不同
        常见的引擎:InnoDB MyISAM Memory/Heap BDB Merge Example CSV MaxDB Archive
        不同的引擎在保存表的结构和数据时采用不同的方式
        MyISAM表文件含义:.frm表定义,.MYD表数据,.MYI表索引
        InnoDB表文件含义:.frm表定义,表空间数据和日志文件
        SHOW ENGINES -- 显示存储引擎的状态信息
        SHOW ENGINE 引擎名 {LOGS|STATUS} -- 显示存储引擎的日志或状态信息
    -- 数据文件目录
        DATA DIRECTORY = '目录'
    -- 索引文件目录
        INDEX DIRECTORY = '目录'
    -- 表注释
        COMMENT = 'string'
    -- 分区选项
        PARTITION BY ... (详细见手册)
-- 查看所有表
    SHOW TABLES[ LIKE 'pattern']
    SHOW TABLES FROM 表名
-- 查看表机构
    SHOW CREATE TABLE 表名    (信息更详细)
    DESC 表名 / DESCRIBE 表名 / EXPLAIN 表名 / SHOW COLUMNS FROM 表名 [LIKE 'PATTERN']
    SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern']
-- 修改表
    -- 修改表本身的选项
        ALTER TABLE 表名 表的选项
        EG:    ALTER TABLE 表名 ENGINE=MYISAM;
    -- 对表进行重命名
        RENAME TABLE 原表名 TO 新表名
        RENAME TABLE 原表名 TO 库名.表名    (可将表移动到另一个数据库)
        -- RENAME可以交换两个表名
    -- 修改表的字段机构
        ALTER TABLE 表名 操作名
        -- 操作名
            ADD[ COLUMN] 字段名        -- 增加字段
                AFTER 字段名            -- 表示增加在该字段名后面
                FIRST                -- 表示增加在第一个
            ADD PRIMARY KEY(字段名)    -- 创建主键
            ADD UNIQUE [索引名] (字段名)-- 创建唯一索引
            ADD INDEX [索引名] (字段名)    -- 创建普通索引
            ADD
            DROP[ COLUMN] 字段名        -- 删除字段
            MODIFY[ COLUMN] 字段名 字段属性        -- 支持对字段属性进行修改,不能修改字段名(所有原有属性也需写上)
            CHANGE[ COLUMN] 原字段名 新字段名 字段属性        -- 支持对字段名修改
            DROP PRIMARY KEY    -- 删除主键(删除主键前需删除其AUTO_INCREMENT属性)
            DROP INDEX 索引名    -- 删除索引
            DROP FOREIGN KEY 外键    -- 删除外键

-- 删除表
    DROP TABLE[ IF EXISTS] 表名 ...
-- 清空表数据
    TRUNCATE [TABLE] 表名
-- 复制表结构
    CREATE TABLE 表名 LIKE 要复制的表名
-- 复制表结构和数据
    CREATE TABLE 表名 [AS] SELECT * FROM 要复制的表名
-- 检查表是否有错误
    CHECK TABLE tbl_name [, tbl_name] ... [option] ...
-- 优化表
    OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
-- 修复表
    REPAIR [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... [QUICK] [EXTENDED] [USE_FRM]
-- 分析表
    ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...



/* 数据操作 */ ------------------
-- 增
    INSERT [INTO] 表名 [(字段列表)] VALUES (值列表)[, (值列表), ...]
        -- 如果要插入的值列表包含所有字段并且顺序一致,则可以省略字段列表。
        -- 可同时插入多条数据记录!
        REPLACE 与 INSERT 完全一样,可互换。
    INSERT [INTO] 表名 SET 字段名=值[, 字段名=值, ...]
-- 查
    SELECT 字段列表 FROM 表名[ 其他子句]
        -- 可来自多个表的多个字段
        -- 其他子句可以不使用
        -- 字段列表可以用*代替,表示所有字段
-- 删
    DELETE FROM 表名[ 删除条件子句]
        没有条件子句,则会删除全部
-- 改
    UPDATE 表名 SET 字段名=新值[, 字段名=新值] [更新条件]

/* 字符集编码 */ ------------------
-- MySQL、数据库、表、字段均可设置编码
-- 数据编码与客户端编码不需一致
SHOW VARIABLES LIKE 'character_set_%'    -- 查看所有字符集编码项
    character_set_client        客户端向服务器发送数据时使用的编码
    character_set_results        服务器端将结果返回给客户端所使用的编码
    character_set_connection    连接层编码
SET 变量名 = 变量值
    set character_set_client = gbk;
    set character_set_results = gbk;
    set character_set_connection = gbk;
SET NAMES GBK;    -- 相当于完成以上三个设置
-- 校对集
    校对集用以排序
    SHOW CHARACTER SET [LIKE 'pattern']/SHOW CHARSET [LIKE 'pattern']    查看所有字符集
    SHOW COLLATION [LIKE 'pattern']        查看所有校对集
    charset 字符集编码        设置字符集编码
    collate 校对集编码        设置校对集编码

/* 数据类型(列类型) */ ------------------
1. 数值类型
-- a. 整型 ----------
    类型            字节        范围(有符号位)
    tinyint        1字节    -128 ~ 127        无符号位:0 ~ 255
    smallint    2字节    -32768 ~ 32767
    mediumint    3字节    -8388608 ~ 8388607
    int            4字节
    bigint        8字节

    int(M)    M表示总位数
    - 默认存在符号位,unsigned 属性修改
    - 显示宽度,如果某个数不够定义字段时设置的位数,则前面以0补填,zerofill 属性修改
        例:int(5)    插入一个数'123',补填后为'00123'
    - 在满足要求的情况下,越小越好。
    - 1表示bool值真,0表示bool值假。MySQL没有布尔类型,通过整型0和1表示。常用tinyint(1)表示布尔型。

-- b. 浮点型 ----------
    类型                字节        范围
    float(单精度)        4字节
    double(双精度)    8字节
    浮点型既支持符号位 unsigned 属性,也支持显示宽度 zerofill 属性。
        不同于整型,前后均会补填0.
    定义浮点型时,需指定总位数和小数位数。
        float(M, D)        double(M, D)
        M表示总位数,D表示小数位数。
        M和D的大小会决定浮点数的范围。不同于整型的固定范围。
        M既表示总位数(不包括小数点和正负号),也表示显示宽度(所有显示符号均包括)。
        支持科学计数法表示。
        浮点数表示近似值。

-- c. 定点数 ----------
    decimal    -- 可变长度
    decimal(M, D)    M也表示总位数,D表示小数位数。
    保存一个精确的数值,不会发生数据的改变,不同于浮点数的四舍五入。
    将浮点数转换为字符串来保存,每9位数字保存为4个字节。

2. 字符串类型
-- a. char, varchar ----------
    char    定长字符串,速度快,但浪费空间
    varchar    变长字符串,速度慢,但节省空间
    M表示能存储的最大长度,此长度是字符数,非字节数。
    不同的编码,所占用的空间不同。
    char,最多255个字符,与编码无关。
    varchar,最多65535字符,与编码有关。
    一条有效记录最大不能超过65535个字节。
        utf8 最大为21844个字符,gbk 最大为32766个字符,latin1 最大为65532个字符
    varchar 是变长的,需要利用存储空间保存 varchar 的长度,如果数据小于255个字节,则采用一个字节来保存长度,反之需要两个字节来保存。
    varchar 的最大有效长度由最大行大小和使用的字符集确定。
    最大有效长度是65532字节,因为在varchar存字符串时,第一个字节是空的,不存在任何数据,然后还需两个字节来存放字符串的长度,所以有效长度是64432-1-2=65532字节。
    例:若一个表定义为 CREATE TABLE tb(c1 int, c2 char(30), c3 varchar(N)) charset=utf8; 问N的最大值是多少? 答:(65535-1-2-4-30*3)/3

-- b. blob, text ----------
    blob 二进制字符串(字节字符串)
        tinyblob, blob, mediumblob, longblob
    text 非二进制字符串(字符字符串)
        tinytext, text, mediumtext, longtext
    text 在定义时,不需要定义长度,也不会计算总长度。
    text 类型在定义时,不可给default值

-- c. binary, varbinary ----------
    类似于char和varchar,用于保存二进制字符串,也就是保存字节字符串而非字符字符串。
    char, varchar, text 对应 binary, varbinary, blob.

3. 日期时间类型
    一般用整型保存时间戳,因为PHP可以很方便的将时间戳进行格式化。
    datetime    8字节    日期及时间        1000-01-01 00:00:00 到 9999-12-31 23:59:59
    date        3字节    日期            1000-01-01 到 9999-12-31
    timestamp    4字节    时间戳        19700101000000 到 2038-01-19 03:14:07
    time        3字节    时间            -838:59:59 到 838:59:59
    year        1字节    年份            1901 - 2155
    
datetime    “YYYY-MM-DD hh:mm:ss”
timestamp    “YY-MM-DD hh:mm:ss”
            “YYYYMMDDhhmmss”
            “YYMMDDhhmmss”
            YYYYMMDDhhmmss
            YYMMDDhhmmss
date        “YYYY-MM-DD”
            “YY-MM-DD”
            “YYYYMMDD”
            “YYMMDD”
            YYYYMMDD
            YYMMDD
time        “hh:mm:ss”
            “hhmmss”
            hhmmss
year        “YYYY”
            “YY”
            YYYY
            YY

4. 枚举和集合
-- 枚举(enum) ----------
enum(val1, val2, val3...)
    在已知的值中进行单选。最大数量为65535.
    枚举值在保存时,以2个字节的整型(smallint)保存。每个枚举值,按保存的位置顺序,从1开始逐一递增。
    表现为字符串类型,存储却是整型。
    NULL值的索引是NULL。
    空字符串错误值的索引值是0。

-- 集合(set) ----------
set(val1, val2, val3...)
    create table tab ( gender set('男', '女', '无') );
    insert into tab values ('男, 女');
    最多可以有64个不同的成员。以bigint存储,共8个字节。采取位运算的形式。
    当创建表时,SET成员值的尾部空格将自动被删除。

/* 选择类型 */
-- PHP角度
1. 功能满足
2. 存储空间尽量小,处理效率更高
3. 考虑兼容问题

-- IP存储 ----------
1. 只需存储,可用字符串
2. 如果需计算,查找等,可存储为4个字节的无符号int,即unsigned
    1) PHP函数转换
        ip2long可转换为整型,但会出现携带符号问题。需格式化为无符号的整型。
        利用sprintf函数格式化字符串
        sprintf("%u", ip2long('192.168.3.134'));
        然后用long2ip将整型转回IP字符串
    2) MySQL函数转换(无符号整型,UNSIGNED)
        INET_ATON('127.0.0.1') 将IP转为整型
        INET_NTOA(2130706433) 将整型转为IP
        



/* 列属性(列约束) */ ------------------
1. 主键
    - 能唯一标识记录的字段,可以作为主键。
    - 一个表只能有一个主键。
    - 主键具有唯一性。
    - 声明字段时,用 primary key 标识。
        也可以在字段列表之后声明
            例:create table tab ( id int, stu varchar(10), primary key (id));
    - 主键字段的值不能为null。
    - 主键可以由多个字段共同组成。此时需要在字段列表后声明的方法。
        例:create table tab ( id int, stu varchar(10), age int, primary key (stu, age));

2. unique 唯一索引(唯一约束)
    使得某字段的值也不能重复。
    
3. null 约束
    null不是数据类型,是列的一个属性。
    表示当前列是否可以为null,表示什么都没有。
    null, 允许为空。默认。
    not null, 不允许为空。
    insert into tab values (null, 'val');
        -- 此时表示将第一个字段的值设为null, 取决于该字段是否允许为null
    
4. default 默认值属性
    当前字段的默认值。
    insert into tab values (default, 'val');    -- 此时表示强制使用默认值。
    create table tab ( add_time timestamp default current_timestamp );
        -- 表示将当前时间的时间戳设为默认值。
        current_date, current_time

5. auto_increment 自动增长约束
    自动增长必须为索引(主键或unique)
    只能存在一个字段为自动增长。
    默认为1开始自动增长。可以通过表属性 auto_increment = x进行设置,或 alter table tbl auto_increment = x;

6. comment 注释
    例:create table tab ( id int ) comment '注释内容';

7. foreign key 外键约束
    用于限制主表与从表数据完整性。
    alter table t1 add constraint `t1_t2_fk` foreign key (t1_id) references t2(id);
        -- 将表t1的t1_id外键关联到表t2的id字段。
        -- 每个外键都有一个名字,可以通过 constraint 指定

    存在外键的表,称之为从表(子表),外键指向的表,称之为主表(父表)。

    作用:保持数据一致性,完整性,主要目的是控制存储在外键表(从表)中的数据。

    MySQL中,可以对InnoDB引擎使用外键约束:
    语法:
    foreign key (外键字段) references 主表名 (关联字段) [主表记录删除时的动作] [主表记录更新时的动作]
    此时需要检测一个从表的外键需要约束为主表的已存在的值。外键在没有关联的情况下,可以设置为null.前提是该外键列,没有not null。

    可以不指定主表记录更改或更新时的动作,那么此时主表的操作被拒绝。
    如果指定了 on update 或 on delete:在删除或更新时,有如下几个操作可以选择:
    1. cascade,级联操作。主表数据被更新(主键值更新),从表也被更新(外键值更新)。主表记录被删除,从表相关记录也被删除。
    2. set null,设置为null。主表数据被更新(主键值更新),从表的外键被设置为null。主表记录被删除,从表相关记录外键被设置成null。但注意,要求该外键列,没有not null属性约束。
    3. restrict,拒绝父表删除和更新。

    注意,外键只被InnoDB存储引擎所支持。其他引擎是不支持的。


/* 建表规范 */ ------------------
    -- Normal Format, NF
        - 每个表保存一个实体信息
        - 每个具有一个ID字段作为主键
        - ID主键 + 原子表
    -- 1NF, 第一范式
        字段不能再分,就满足第一范式。
    -- 2NF, 第二范式
        满足第一范式的前提下,不能出现部分依赖。
        消除符合主键就可以避免部分依赖。增加单列关键字。
    -- 3NF, 第三范式
        满足第二范式的前提下,不能出现传递依赖。
        某个字段依赖于主键,而有其他字段依赖于该字段。这就是传递依赖。
        将一个实体信息的数据放在一个表内实现。


/* select */ ------------------

select [all|distinct] select_expr from -> where -> group by [合计函数] -> having -> order by -> limit

a. select_expr
    -- 可以用 * 表示所有字段。
        select * from tb;
    -- 可以使用表达式(计算公式、函数调用、字段也是个表达式)
        select stu, 29+25, now() from tb;
    -- 可以为每个列使用别名。适用于简化列标识,避免多个列标识符重复。
        - 使用 as 关键字,也可省略 as.
        select stu+10 as add10 from tb;

b. from 子句
    用于标识查询来源。
    -- 可以为表起别名。使用as关键字。
        select * from tb1 as tt, tb2 as bb;
    -- from子句后,可以同时出现多个表。
        -- 多个表会横向叠加到一起,而数据会形成一个笛卡尔积。
        select * from tb1, tb2;

c. where 子句
    -- 从from获得的数据源中进行筛选。
    -- 整型1表示真,0表示假。
    -- 表达式由运算符和运算数组成。
        -- 运算数:变量(字段)、值、函数返回值
        -- 运算符:
            =, <=>, <>, !=, <=, <, >=, >, !, &&, ||,
            in (not) null, (not) like, (not) in, (not) between and, is (not), and, or, not, xor
            is/is not 加上ture/false/unknown,检验某个值的真假
            <=>与<>功能相同,<=>可用于null比较

d. group by 子句, 分组子句
    group by 字段/别名 [排序方式]
    分组后会进行排序。升序:ASC,降序:DESC
    
    以下[合计函数]需配合 group by 使用:
    count 返回不同的非NULL值数目    count(*)、count(字段)
    sum 求和
    max 求最大值
    min 求最小值
    avg 求平均值
    group_concat 返回带有来自一个组的连接的非NULL值的字符串结果。组内字符串连接。

e. having 子句,条件子句
    与 where 功能、用法相同,执行时机不同。
    where 在开始时执行检测数据,对原数据进行过滤。
    having 对筛选出的结果再次进行过滤。
    having 字段必须是查询出来的,where 字段必须是数据表存在的。
    where 不可以使用字段的别名,having 可以。因为执行WHERE代码时,可能尚未确定列值。
    where 不可以使用合计函数。一般需用合计函数才会用 having
    SQL标准要求HAVING必须引用GROUP BY子句中的列或用于合计函数中的列。

f. order by 子句,排序子句
    order by 排序字段/别名 排序方式 [,排序字段/别名 排序方式]...
    升序:ASC,降序:DESC
    支持多个字段的排序。

g. limit 子句,限制结果数量子句
    仅对处理好的结果进行数量限制。将处理好的结果的看作是一个集合,按照记录出现的顺序,索引从0开始。
    limit 起始位置, 获取条数
    省略第一个参数,表示从索引0开始。limit 获取条数

h. distinct, all 选项
    distinct 去除重复记录
    默认为 all, 全部记录


/* UNION */ ------------------
    将多个select查询的结果组合成一个结果集合。
    SELECT ... UNION [ALL|DISTINCT] SELECT ...
    默认 DISTINCT 方式,即所有返回的行都是唯一的
    建议,对每个SELECT查询加上小括号包裹。
    ORDER BY 排序时,需加上 LIMIT 进行结合。
    需要各select查询的字段数量一样。
    每个select查询的字段列表(数量、类型)应一致,因为结果中的字段名以第一条select语句为准。


/* 子查询 */ ------------------
    - 子查询需用括号包裹。
-- from型
    from后要求是一个表,必须给子查询结果取个别名。
    - 简化每个查询内的条件。
    - from型需将结果生成一个临时表格,可用以原表的锁定的释放。
    - 子查询返回一个表,表型子查询。
    select * from (select * from tb where id>0) as subfrom where id>1;
-- where型
    - 子查询返回一个值,标量子查询。
    - 不需要给子查询取别名。
    - where子查询内的表,不能直接用以更新。
    select * from tb where money = (select max(money) from tb);
    -- 列子查询
        如果子查询结果返回的是一列。
        使用 in 或 not in 完成查询
        exists 和 not exists 条件
            如果子查询返回数据,则返回1或0。常用于判断条件。
            select column1 from t1 where exists (select * from t2);
    -- 行子查询
        查询条件是一个行。
        select * from t1 where (id, gender) in (select id, gender from t2);
        行构造符:(col1, col2, ...) 或 ROW(col1, col2, ...)
        行构造符通常用于与对能返回两个或两个以上列的子查询进行比较。

    -- 特殊运算符
    != all()    相当于 not in
    = some()    相当于 in。any 是 some 的别名
    != some()    不等同于 not in,不等于其中某一个。
    all, some 可以配合其他运算符一起使用。


/* 连接查询(join) */ ------------------
    将多个表的字段进行连接,可以指定连接条件。
-- 内连接(inner join)
    - 默认就是内连接,可省略inner。
    - 只有数据存在时才能发送连接。即连接结果不能出现空行。
    on 表示连接条件。其条件表达式与where类似。也可以省略条件(表示条件永远为真)
    也可用where表示连接条件。
    还有 using, 但需字段名相同。 using(字段名)

    -- 交叉连接 cross join
        即,没有条件的内连接。
        select * from tb1 cross join tb2;
-- 外连接(outer join)
    - 如果数据不存在,也会出现在连接结果中。
    -- 左外连接 left join
        如果数据不存在,左表记录会出现,而右表为null填充
    -- 右外连接 right join
        如果数据不存在,右表记录会出现,而左表为null填充
-- 自然连接(natural join)
    自动判断连接条件完成连接。
    相当于省略了using,会自动查找相同字段名。
    natural join
    natural left join
    natural right join

select info.id, info.name, info.stu_num, extra_info.hobby, extra_info.sex from info, extra_info where info.stu_num = extra_info.stu_id;

/* 导入导出 */ ------------------
select * into outfile 文件地址 [控制格式] from 表名;    -- 导出表数据
load data [local] infile 文件地址 [replace|ignore] into table 表名 [控制格式];    -- 导入数据
    生成的数据默认的分隔符是制表符
    local未指定,则数据文件必须在服务器上
    replace 和 ignore 关键词控制对现有的唯一键记录的重复的处理
-- 控制格式
fields    控制字段格式
默认:fields terminated by '\t' enclosed by '' escaped by '\\'
    terminated by 'string'    -- 终止
    enclosed by 'char'        -- 包裹
    escaped by 'char'        -- 转义
    -- 示例:
        SELECT a,b,a+b INTO OUTFILE '/tmp/result.text'
        FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
        LINES TERMINATED BY '\n'
        FROM test_table;
lines    控制行格式
默认:lines terminated by '\n'
    terminated by 'string'    -- 终止
    
/* insert */ ------------------
select语句获得的数据可以用insert插入。

可以省略对列的指定,要求 values () 括号内,提供给了按照列顺序出现的所有字段的值。
    或者使用set语法。
    insert into tbl_name set field=value,...;

可以一次性使用多个值,采用(), (), ();的形式。
    insert into tbl_name values (), (), ();

可以在列值指定时,使用表达式。
    insert into tbl_name values (field_value, 10+10, now());
可以使用一个特殊值 default,表示该列使用默认值。
    insert into tbl_name values (field_value, default);

可以通过一个查询的结果,作为需要插入的值。
    insert into tbl_name select ...;

可以指定在插入的值出现主键(或唯一索引)冲突时,更新其他非主键列的信息。
    insert into tbl_name values/set/select on duplicate key update 字段=值, …;

/* delete */ ------------------
DELETE FROM tbl_name [WHERE where_definition] [ORDER BY ...] [LIMIT row_count]

按照条件删除

指定删除的最多记录数。Limit

可以通过排序条件删除。order by + limit

支持多表删除,使用类似连接语法。
delete from 需要删除数据多表1,表2 using 表连接操作 条件。

/* truncate */ ------------------
TRUNCATE [TABLE] tbl_name
清空数据
删除重建表

区别:
1,truncate 是删除表再创建,delete 是逐条删除
2,truncate 重置auto_increment的值。而delete不会
3,truncate 不知道删除了几条,而delete知道。
4,当被用于带分区的表时,truncate 会保留分区


/* 备份与还原 */ ------------------
备份,将数据的结构与表内数据保存起来。
利用 mysqldump 指令完成。

-- 导出
1. 导出一张表
  mysqldump -u用户名 -p密码 库名 表名 > 文件名(D:/a.sql)
2. 导出多张表
  mysqldump -u用户名 -p密码 库名 表1 表2 表3 > 文件名(D:/a.sql)
3. 导出所有表
  mysqldump -u用户名 -p密码 库名 > 文件名(D:/a.sql)
4. 导出一个库
  mysqldump -u用户名 -p密码 -B 库名 > 文件名(D:/a.sql)

可以-w携带备份条件

-- 导入
1. 在登录mysql的情况下:
  source  备份文件
2. 在不登录的情况下
  mysql -u用户名 -p密码 库名 < 备份文件


/* 视图 */ ------------------
什么是视图:
    视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。
    视图具有表结构文件,但不存在数据文件。
    对其中所引用的基础表来说,视图的作用类似于筛选。定义视图的筛选可以来自当前或其它数据库的一个或多个表,或者其它视图。通过视图进行查询没有任何限制,通过它们进行数据修改时的限制也很少。
    视图是存储在数据库中的查询的sql语句,它主要出于两种原因:安全原因,视图可以隐藏一些数据,如:社会保险基金表,可以用视图只显示姓名,地址,而不显示社会保险号和工资数等,另一原因是可使复杂的查询易于理解和使用。

-- 创建视图
CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW view_name [(column_list)] AS select_statement
    - 视图名必须唯一,同时不能与表重名。
    - 视图可以使用select语句查询到的列名,也可以自己指定相应的列名。
    - 可以指定视图执行的算法,通过ALGORITHM指定。
    - column_list如果存在,则数目必须等于SELECT语句检索的列数

-- 查看结构
    SHOW CREATE VIEW view_name

-- 删除视图
    - 删除视图后,数据依然存在。
    - 可同时删除多个视图。
    DROP VIEW [IF EXISTS] view_name ...

-- 修改视图结构
    - 一般不修改视图,因为不是所有的更新视图都会映射到表上。
    ALTER VIEW view_name [(column_list)] AS select_statement

-- 视图作用
    1. 简化业务逻辑
    2. 对客户端隐藏真实的表结构

-- 视图算法(ALGORITHM)
    MERGE        合并
        将视图的查询语句,与外部查询需要先合并再执行!
    TEMPTABLE    临时表
        将视图执行完毕后,形成临时表,再做外层查询!
    UNDEFINED    未定义(默认),指的是MySQL自主去选择相应的算法。



/* 事务(transaction) */ ------------------
事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败。
    - 支持连续SQL的集体成功或集体撤销。
    - 事务是数据库在数据晚自习方面的一个功能。
    - 需要利用 InnoDB 或 BDB 存储引擎,对自动提交的特性支持完成。
    - InnoDB被称为事务安全型引擎。

-- 事务开启
    START TRANSACTION; 或者 BEGIN;
    开启事务后,所有被执行的SQL语句均被认作当前事务内的SQL语句。
-- 事务提交
    COMMIT;
-- 事务回滚
    ROLLBACK;
    如果部分操作发生问题,映射到事务开启前。

-- 事务的特性
    1. 原子性(Atomicity)
        事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
    2. 一致性(Consistency)
        事务前后数据的完整性必须保持一致。
        - 事务开始和结束时,外部数据一致
        - 在整个事务过程中,操作是连续的
    3. 隔离性(Isolation)
        多个用户并发访问数据库时,一个用户的事务不能被其它用户的事物所干扰,多个并发事务之间的数据要相互隔离。
    4. 持久性(Durability)
        一个事务一旦被提交,它对数据库中的数据改变就是永久性的。

-- 事务的实现
    1. 要求是事务支持的表类型
    2. 执行一组相关的操作前开启事务
    3. 整组操作完成后,都成功,则提交;如果存在失败,选择回滚,则会回到事务开始的备份点。

-- 事务的原理
    利用InnoDB的自动提交(autocommit)特性完成。
    普通的MySQL执行语句后,当前的数据提交操作均可被其他客户端可见。
    而事务是暂时关闭“自动提交”机制,需要commit提交持久化数据操作。

-- 注意
    1. 数据定义语言(DDL)语句不能被回滚,比如创建或取消数据库的语句,和创建、取消或更改表或存储的子程序的语句。
    2. 事务不能被嵌套

-- 保存点
    SAVEPOINT 保存点名称 -- 设置一个事务保存点
    ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点
    RELEASE SAVEPOINT 保存点名称 -- 删除保存点

-- InnoDB自动提交特性设置
    SET autocommit = 0|1;    0表示关闭自动提交,1表示开启自动提交。
    - 如果关闭了,那普通操作的结果对其他客户端也不可见,需要commit提交后才能持久化数据操作。
    - 也可以关闭自动提交来开启事务。但与START TRANSACTION不同的是,
        SET autocommit是永久改变服务器的设置,直到下次再次修改该设置。(针对当前连接)
        而START TRANSACTION记录开启前的状态,而一旦事务提交或回滚后就需要再次开启事务。(针对当前事务)


/* 锁表 */
表锁定只用于防止其它客户端进行不正当地读取和写入
MyISAM 支持表锁,InnoDB 支持行锁
-- 锁定
    LOCK TABLES tbl_name [AS alias]
-- 解锁
    UNLOCK TABLES


/* 触发器 */ ------------------
    触发程序是与表有关的命名数据库对象,当该表出现特定事件时,将激活该对象
    监听:记录的增加、修改、删除。

-- 创建触发器
CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt
    参数:
    trigger_time是触发程序的动作时间。它可以是 before 或 after,以指明触发程序是在激活它的语句之前或之后触发。
    trigger_event指明了激活触发程序的语句的类型
        INSERT:将新行插入表时激活触发程序
        UPDATE:更改某一行时激活触发程序
        DELETE:从表中删除某一行时激活触发程序
    tbl_name:监听的表,必须是永久性的表,不能将触发程序与TEMPORARY表或视图关联起来。
    trigger_stmt:当触发程序激活时执行的语句。执行多个语句,可使用BEGIN...END复合语句结构

-- 删除
DROP TRIGGER [schema_name.]trigger_name

可以使用old和new代替旧的和新的数据
    更新操作,更新前是old,更新后是new.
    删除操作,只有old.
    增加操作,只有new.

-- 注意
    1. 对于具有相同触发程序动作时间和事件的给定表,不能有两个触发程序。


-- 字符连接函数
concat(str1[, str2,...])

-- 分支语句
if 条件 then
    执行语句
elseif 条件 then
    执行语句
else
    执行语句
end if;

-- 修改最外层语句结束符
delimiter 自定义结束符号
    SQL语句
自定义结束符号

delimiter ;        -- 修改回原来的分号

-- 语句块包裹
begin
    语句块
end

-- 特殊的执行
1. 只要添加记录,就会触发程序。
2. Insert into on duplicate key update 语法会触发:
    如果没有重复记录,会触发 before insert, after insert;
    如果有重复记录并更新,会触发 before insert, before update, after update;
    如果有重复记录但是没有发生更新,则触发 before insert, before update
3. Replace 语法 如果有记录,则执行 before insert, before delete, after delete, after insert


/* SQL编程 */ ------------------

--// 局部变量 ----------
-- 变量声明
    declare var_name[,...] type [default value]
    这个语句被用来声明局部变量。要给变量提供一个默认值,请包含一个default子句。值可以被指定为一个表达式,不需要为一个常数。如果没有default子句,初始值为null。

-- 赋值
    使用 set 和 select into 语句为变量赋值。

    - 注意:在函数内是可以使用全局变量(用户自定义的变量)


--// 全局变量 ----------
-- 定义、赋值
set 语句可以定义并为变量赋值。
set @var = value;
也可以使用select into语句为变量初始化并赋值。这样要求select语句只能返回一行,但是可以是多个字段,就意味着同时为多个变量进行赋值,变量的数量需要与查询的列数一致。
还可以把赋值语句看作一个表达式,通过select执行完成。此时为了避免=被当作关系运算符看待,使用:=代替。(set语句可以使用= 和 :=)。
select @var:=20;
select @v1:=id, @v2=name from t1 limit 1;
select * from tbl_name where @var:=30;

select into 可以将表中查询获得的数据赋给变量。
    -| select max(height) into @max_height from tb;

-- 自定义变量名
为了避免select语句中,用户自定义的变量与系统标识符(通常是字段名)冲突,用户自定义变量在变量名前使用@作为开始符号。
@var=10;

    - 变量被定义后,在整个会话周期都有效(登录到退出)


--// 控制结构 ----------
-- if语句
if search_condition then
    statement_list    
[elseif search_condition then
    statement_list]
...
[else
    statement_list]
end if;

-- case语句
CASE value WHEN [compare-value] THEN result
[WHEN [compare-value] THEN result ...]
[ELSE result]
END


-- while循环
[begin_label:] while search_condition do
    statement_list
end while [end_label];

- 如果需要在循环内提前终止 while循环,则需要使用标签;标签需要成对出现。

    -- 退出循环
        退出整个循环 leave
        退出当前循环 iterate
        通过退出的标签决定退出哪个循环


--// 内置函数 ----------
-- 数值函数
abs(x)            -- 绝对值 abs(-10.9) = 10
format(x, d)    -- 格式化千分位数值 format(1234567.456, 2) = 1,234,567.46
ceil(x)            -- 向上取整 ceil(10.1) = 11
floor(x)        -- 向下取整 floor (10.1) = 10
round(x)        -- 四舍五入去整
mod(m, n)        -- m%n m mod n 求余 10%3=1
pi()            -- 获得圆周率
pow(m, n)        -- m^n
sqrt(x)            -- 算术平方根
rand()            -- 随机数
truncate(x, d)    -- 截取d位小数

-- 时间日期函数
now(), current_timestamp();     -- 当前日期时间
current_date();                    -- 当前日期
current_time();                    -- 当前时间
date('yyyy-mm-dd hh:ii:ss');    -- 获取日期部分
time('yyyy-mm-dd hh:ii:ss');    -- 获取时间部分
date_format('yyyy-mm-dd hh:ii:ss', '%d %y %a %d %m %b %j');    -- 格式化时间
unix_timestamp();                -- 获得unix时间戳
from_unixtime();                -- 从时间戳获得时间

-- 字符串函数
length(string)            -- string长度,字节
char_length(string)        -- string的字符个数
substring(str, position [,length])        -- 从str的position开始,取length个字符
replace(str ,search_str ,replace_str)    -- 在str中用replace_str替换search_str
instr(string ,substring)    -- 返回substring首次在string中出现的位置
concat(string [,...])    -- 连接字串
charset(str)            -- 返回字串字符集
lcase(string)            -- 转换成小写
left(string, length)    -- 从string2中的左边起取length个字符
load_file(file_name)    -- 从文件读取内容
locate(substring, string [,start_position])    -- 同instr,但可指定开始位置
lpad(string, length, pad)    -- 重复用pad加在string开头,直到字串长度为length
ltrim(string)            -- 去除前端空格
repeat(string, count)    -- 重复count次
rpad(string, length, pad)    --在str后用pad补充,直到长度为length
rtrim(string)            -- 去除后端空格
strcmp(string1 ,string2)    -- 逐字符比较两字串大小

-- 流程函数
case when [condition] then result [when [condition] then result ...] [else result] end   多分支
if(expr1,expr2,expr3)  双分支。

-- 聚合函数
count()
sum();
max();
min();
avg();
group_concat()

-- 其他常用函数
md5();
default();


--// 存储函数,自定义函数 ----------
-- 新建
    CREATE FUNCTION function_name (参数列表) RETURNS 返回值类型
        函数体

    - 函数名,应该合法的标识符,并且不应该与已有的关键字冲突。
    - 一个函数应该属于某个数据库,可以使用db_name.funciton_name的形式执行当前函数所属数据库,否则为当前数据库。
    - 参数部分,由"参数名"和"参数类型"组成。多个参数用逗号隔开。
    - 函数体由多条可用的mysql语句,流程控制,变量声明等语句构成。
    - 多条语句应该使用 begin...end 语句块包含。
    - 一定要有 return 返回值语句。

-- 删除
    DROP FUNCTION [IF EXISTS] function_name;

-- 查看
    SHOW FUNCTION STATUS LIKE 'partten'
    SHOW CREATE FUNCTION function_name;

-- 修改
    ALTER FUNCTION function_name 函数选项


--// 存储过程,自定义功能 ----------
-- 定义
存储存储过程 是一段代码(过程),存储在数据库中的sql组成。
一个存储过程通常用于完成一段业务逻辑,例如报名,交班费,订单入库等。
而一个函数通常专注与某个功能,视为其他程序服务的,需要在其他语句中调用函数才可以,而存储过程不能被其他调用,是自己执行 通过call执行。

-- 创建
CREATE PROCEDURE sp_name (参数列表)
    过程体

参数列表:不同于函数的参数列表,需要指明参数类型
IN,表示输入型
OUT,表示输出型
INOUT,表示混合型

注意,没有返回值。


/* 存储过程 */ ------------------
存储过程是一段可执行性代码的集合。相比函数,更偏向于业务逻辑。
调用:CALL 过程名
-- 注意
- 没有返回值。
- 只能单独调用,不可夹杂在其他语句中

-- 参数
IN|OUT|INOUT 参数名 数据类型
IN        输入:在调用过程中,将数据输入到过程体内部的参数
OUT        输出:在调用过程中,将过程体处理完的结果返回到客户端
INOUT    输入输出:既可输入,也可输出

-- 语法
CREATE PROCEDURE 过程名 (参数列表)
BEGIN
    过程体
END


/* 用户和权限管理 */ ------------------
用户信息表:mysql.user
-- 刷新权限
FLUSH PRIVILEGES
-- 增加用户
CREATE USER 用户名 IDENTIFIED BY [PASSWORD] 密码(字符串)
    - 必须拥有mysql数据库的全局CREATE USER权限,或拥有INSERT权限。
    - 只能创建用户,不能赋予权限。
    - 用户名,注意引号:如 'user_name'@'192.168.1.1'
    - 密码也需引号,纯数字密码也要加引号
    - 要在纯文本中指定密码,需忽略PASSWORD关键词。要把密码指定为由PASSWORD()函数返回的混编值,需包含关键字PASSWORD
-- 重命名用户
RENAME USER old_user TO new_user
-- 设置密码
SET PASSWORD = PASSWORD('密码')    -- 为当前用户设置密码
SET PASSWORD FOR 用户名 = PASSWORD('密码')    -- 为指定用户设置密码
-- 删除用户
DROP USER 用户名
-- 分配权限/添加用户
GRANT 权限列表 ON 表名 TO 用户名 [IDENTIFIED BY [PASSWORD] 'password']
    - all privileges 表示所有权限
    - *.* 表示所有库的所有表
    - 库名.表名 表示某库下面的某表
-- 查看权限
SHOW GRANTS FOR 用户名
    -- 查看当前用户权限
    SHOW GRANTS; 或 SHOW GRANTS FOR CURRENT_USER; 或 SHOW GRANTS FOR CURRENT_USER();
-- 撤消权限
REVOKE 权限列表 ON 表名 FROM 用户名
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 用户名    -- 撤销所有权限
-- 权限层级
-- 要使用GRANT或REVOKE,您必须拥有GRANT OPTION权限,并且您必须用于您正在授予或撤销的权限。
全局层级:全局权限适用于一个给定服务器中的所有数据库,mysql.user
    GRANT ALL ON *.*和 REVOKE ALL ON *.*只授予和撤销全局权限。
数据库层级:数据库权限适用于一个给定数据库中的所有目标,mysql.db, mysql.host
    GRANT ALL ON db_name.*和REVOKE ALL ON db_name.*只授予和撤销数据库权限。
表层级:表权限适用于一个给定表中的所有列,mysql.talbes_priv
    GRANT ALL ON db_name.tbl_name和REVOKE ALL ON db_name.tbl_name只授予和撤销表权限。
列层级:列权限适用于一个给定表中的单一列,mysql.columns_priv
    当使用REVOKE时,您必须指定与被授权列相同的列。
-- 权限列表
ALL [PRIVILEGES]    -- 设置除GRANT OPTION之外的所有简单权限
ALTER    -- 允许使用ALTER TABLE
ALTER ROUTINE    -- 更改或取消已存储的子程序
CREATE    -- 允许使用CREATE TABLE
CREATE ROUTINE    -- 创建已存储的子程序
CREATE TEMPORARY TABLES        -- 允许使用CREATE TEMPORARY TABLE
CREATE USER        -- 允许使用CREATE USER, DROP USER, RENAME USER和REVOKE ALL PRIVILEGES。
CREATE VIEW        -- 允许使用CREATE VIEW
DELETE    -- 允许使用DELETE
DROP    -- 允许使用DROP TABLE
EXECUTE        -- 允许用户运行已存储的子程序
FILE    -- 允许使用SELECT...INTO OUTFILE和LOAD DATA INFILE
INDEX     -- 允许使用CREATE INDEX和DROP INDEX
INSERT    -- 允许使用INSERT
LOCK TABLES        -- 允许对您拥有SELECT权限的表使用LOCK TABLES
PROCESS     -- 允许使用SHOW FULL PROCESSLIST
REFERENCES    -- 未被实施
RELOAD    -- 允许使用FLUSH
REPLICATION CLIENT    -- 允许用户询问从属服务器或主服务器的地址
REPLICATION SLAVE    -- 用于复制型从属服务器(从主服务器中读取二进制日志事件)
SELECT    -- 允许使用SELECT
SHOW DATABASES    -- 显示所有数据库
SHOW VIEW    -- 允许使用SHOW CREATE VIEW
SHUTDOWN    -- 允许使用mysqladmin shutdown
SUPER    -- 允许使用CHANGE MASTER, KILL, PURGE MASTER LOGS和SET GLOBAL语句,mysqladmin debug命令;允许您连接(一次),即使已达到max_connections。
UPDATE    -- 允许使用UPDATE
USAGE    -- “无权限”的同义词
GRANT OPTION    -- 允许授予权限


/* 表维护 */
-- 分析和存储表的关键字分布
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE 表名 ...
-- 检查一个或多个表是否有错误
CHECK TABLE tbl_name [, tbl_name] ... [option] ...
option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}
-- 整理数据文件的碎片
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...


/* 杂项 */ ------------------
1. 可用反引号(`)为标识符(库名、表名、字段名、索引、别名)包裹,以避免与关键字重名!中文也可以作为标识符!
2. 每个库目录存在一个保存当前数据库的选项文件db.opt。
3. 注释:
    单行注释 # 注释内容
    多行注释 /* 注释内容 */
    单行注释 -- 注释内容        (标准SQL注释风格,要求双破折号后加一空格符(空格、TAB、换行等))
4. 模式通配符:
    _    任意单个字符
    %    任意多个字符,甚至包括零字符
    单引号需要进行转义 \'
5. CMD命令行内的语句结束符可以为 ";", "\G", "\g",仅影响显示结果。其他地方还是用分号结束。delimiter 可修改当前对话的语句结束符。
6. SQL对大小写不敏感
7. 清除已有语句:\c

 

文章原文:http://www.cnblogs.com/shockerli/p/1000-plus-line-mysql-notes.html

作者:fu415037685 发表于2016/7/23 22:37:54 原文链接
阅读:15 评论:0 查看评论

Python学习笔记14:Python执行环境

$
0
0

1、可调用对象
许多Python对象都是可调用的,即任何能通过函数操作符“()”来调用的对象。Python有4种可调用对象:函数方法以及一些类实例,这些对象的任何引用或者别名都是可调用的。
(1)函数(3种函数)
①内建函数(BIF,built-in function)
由C/C++写的,编译过后放入Python解释器,然后把它们作为第一(内建)名称空间的一部分加载进系统。这些函数在_builtin_模块里,并作为_builtins_模块导入到解释器中。
内建函数的属性包括:

  • bif.__doc__ 文档字符串(或None)
  • bif.__name__ 字符串类型的函数名字
  • bif.__self__ 设置为None(保留给内建方法)
  • bif.__module__ 存放bif定义的模块名字(或None)

②用户定义的函数(UDF,user-defined function)
用Python书写,定义在模块的最高级,作为全局名称空间的一部分装载进系统。函数也可以在其他的函数体内定义。
UDF也有许多属性,最常见的属性有:

  • udf.__doc__ 文档字符串(也可以用udf.func_doc)
  • udf.__name__ 字符串类型的函数名字(也可以用udf.func_name)
  • udf.func_code 字节编译的代码对象
  • udf.func_defaults 默认的参数元组
  • udf.func_globals 全局名称空间字典,和从函数体内部调用globals(x)一样
  • udf.func_dict 函数属性的名称空间
  • udf.func_doc 见udf.__doc__
  • udf.func_name 见udf.__name__
  • udf.func_closure 包含了自由变量的引用的单元对象元组

③lambda表达式
用lambda关键字创建,不使用def语句创建。
用lambda关键字创建的函数对象没有命名,需要将其引用赋值给一个变量才可以被调用。
用lambda关键字创建的函数对象和用户自定义函数具有相同的属性。
(2)方法
①内建方法(BIM)
只有内建类型(BIT)才有内建方法。BIM和BIF调用type()具有相同的结果builtin_function_or_method。BIM和BIF两者都具有相同属性。不同之处在于BIM的__self__属性指向一个Python对象,而BIF指向None。
对于类和实例都可以以该对象为参数调用内建函数dir()来获得它们的数据和方法属性。
②用户定义的方法(UDM)
包含在类定义之中,仅有定义它们的类可以使用。
UDM与类对象是关联的(非绑定方法),但是只能通过类的实例来调用(绑定方法)。
无论UDM是否绑定,所有的UDM都是相同的类型–“实例方法”。
UDM包含的常用属性有:

  • udm.__doc__ 文档字符串(与udm.im_func.__doc__相同)
  • udm.__name__ 字符串类型的方法名字(与udm.im_func.__name__相同)
  • udm.__module__ 定义udm的模块的名字(或None)
  • udm.im_class 方法相关联的类(对于绑定的方法;如果是非绑定,那么为要求udm的类)
  • udm.im_func 方法的函数对象
  • udm.im_self 如果绑定的话为相关联的实例,如果非绑定为None

(3)类
“调用”类的结果便是创建了类实例。程序员可以通过实现__init__()方法来自定义实例化过程。调用类时,传入的参数都会交给__init__()方法。
(4)类的实例
Python给类提供了特殊方法__call__(),允许程序员创建可调用的对象(实例)。
默认情况下,该方法没有实现,所以大多数实例是不可调用的。在类定义中覆盖该方法,类的实例就成为可调用对象了。

class C(object):
    def __call__(self,*args):
        ....
>>>c=C()
>>>c() #调用实例

调用这样的实例对象等同于调用__call__()方法。任何实例调用中给出的参数都会传入到__call__()方法中。
c()与c.__call__(c)的效果相同,c(arg)与c.__call__(c,arg)一样,这里c也作为参数出现,是因为实例将作为每次方法调用的第一个参数。

2、代码对象
可调用物代码对象构成。
代码对象包括语句赋值表达式其他可调用物组成。
代码对象可以作为函数或者方法调用的一部分来执行,也可用exec语句或内建函数eval()来执行。代码对象转换成字节码,然后包含执行环境(可调用物),便可以执行了。

3、可执行的对象声明和内建函数
(1)callable()
布尔函数,确定一个对象是否可以通过函数操作符(())来调用。
(2)compile()
允许在运行时刻迅速生成代码对象,然后调用exec语句或内建函数eval()来执行它们。exec语句和eval()内建函数都可以执行字符串形式的代码。compile()函数提供一次性字节代码预编译。

compile(string,file,type)

string表示要编译的Python代码,必须。
file表示存放代码对象的文件的名字(字符串类型),虽然必须,但是通常被置为空。
type表示代码对象的类型,有三个可能值:

  • eval’:可求值的表达式(与eval()一起使用)
  • ‘single’:单一可执行语句(与exec一起使用)
  • ‘exec’:可执行语句组(与exec一起使用)

(3)eval()
对表达式求值,可以是字符串或内建函数compile()创建的预编译代码对象。

eval(obj,globals=globals(),locals=locals())

obj 是字符串或是已编译为代码对象的表达式,globals必须是字典,locals可以是任意的映射对象。
(4)exec语句
exec语句执行代码对象或字符串形式的Python代码。exec语句还可以接受有效的Python文件对象。一旦执行完毕,继续对exec的调用就会失败,因为第一次执行时,exec已经从文件中读取了全部的数据且停留在文件的末尾,可以使用file对象的tell()方法来告诉我们处于文件的何处,同时使用os.path.getsiae()来告诉我们文件的大小,就可以确认是否到达了文件的末尾。可以调用file对象的seek()方法到文件的开头再次调用exec。

exec obj

(5)input()
它是eval()和raw_input()的组合,等价于eval(raw_input())。将用户输入作为Python表达式进行求值。input()方法返回一个Python对象。

4、执行其他(Python)程序
(1)导入
第一次导入模块会执行模块最高级的代码。
Python解释器通过检测__name__来确定是否要调用脚本,比如”if __name__==’__main__’”,若相等的话,脚本会执行main内代码,否则只是打算导入这个脚本。
(2)execfile()

execfile(filename,globals=globals(),locals=locals())

语法类似于eval()函数,globals和locals都是可选的,如果不提供globals和locals,默认为执行环境的名称空间;如果只提供globals,locals默认和globals相同。
方法1:

f=open(filename,'r')
exec f 
f.close()

<=>
方法2:

execfile(filename)

方法1仅可以在现有的执行环境下运行,方法2可以在指定的全局和局部的名称空间执行。
(3)将模块作为脚本执行(2.4)
①从shell或DOS提示符直接把模块作为脚本来执行

$ myScript.py #从工作目录调用脚本

or

$ python myScript.py 

②如果模块是标准库的一部分,安装在site-packages里,或者仅仅是包里面的模块,需要提供完整路径或借助于Python的导入机制来工作。

$ python -c "import CGIHTTPServer;CGIHTTPServer.test()" #让Python导入机制工作

or

$ python /usr/local/lib/python2x/CGIHTTPServer.py #完整路径名

or

$ python -m CGIHTTPServer #能在类库中执行作为脚本的模块而不是作为导入

5、执行其他(非Python)程序
其他的非Python程序包括:二进制可执行文件其他的shell脚本等。
执行其他的非Python程序需要满足的条件:一个有效的执行环境,如允许文件访问和执行,脚本文件必须能访问它们的解释器,二进制必须是可访问的。
(1)os.system()
接受字符串形式的系统命令并执行它。
执行命令时,Python运行被挂起,命令执行完,将以system()的返回值形式给出退出状态,Python继续执行。
system()通常和不会产生输出的命令一起使用,如压缩或转换文件的程序,挂载磁盘到系统的程序,返回值0表示成功,非0表示其他类型的错误。
(2)os.popen()
popen()函数是文件对象和system()函数的结合。

>>>f=os.popen('uname -a')
>>>data=f.readline()
>>>f.close()
>>>print data

popen()的工作方式和system()相同,但可以通过指向程序的单向连接,像访问文件一样访问程序。popen()返回一个类文件对象。
(3)os.fork(),os.exec*(),os.wait*()
os.fork()创建一个和父进程并行的子进程(通常来说和exec*()一起使用);返回两次…一次给父进程一次给子进程
os.exec*()函数装载文件或者命令,并用参数列表来执行它。
(4)os.spawn*()
(5)subprocess模块

**6、结束执行
(1)sys.exit()和SystemExit异常**
sys.exit()立即退出程序并返回调用程序。当调用sys.exit()时,就会引发SystemExit异常。SystemExit异常是唯一不看作错误的异常,仅仅表示要退出Python的愿望。sys.exit()经常用在命令调用的中途发现错误之后。
(2)sys.exitfunc()
当调用了exit()函数并在解释器退出之前,就会用到这个函数,该函数默认是不可用的,你可以改写它以提供额外的功能。
(3)os._exit()函数
os._exit()函数与sys.exit()和sys.exitfunc()相反,根本不执行任何清理便立即退出Python。
os._exit(status) 状态参数status是必需的。通过sys.exit()退出是解释器的首选方法。
(4)os.kill()函数
kill()函数模拟传统的unix函数发送信号给进程,参数为进程标识符(PID)和信号,信号常见的有SIGINT,SIGQUIT,SIGKILL等。

7、各种操作系统接口
除非标明适用于Windows环境,否则只适用于POSIX系统。

  • uname() 获得系统信息(主机名、操作系统版本、补丁级别、系统结构等)
  • getuid()/setuid(uid) 获取/设置现在进程的真正的用户ID
  • getpid()/getppid() 获取真正的现在/父进程ID(PID)(Win32)
  • getgid()/setgid(gid) 获取/设置现在进程的群组ID
  • getsid()/setsid() 获取会话ID(SID)或创建和返回新的SID
  • umask(mask) 设置现在的数字umask,同时返回先前的那个(mask用于文件许可)(Win32)
  • getenv(ev)/putenv(ev,value),environ获取和设置环境变量ev的值;
  • os.environ属性是描述当前所有环境变量的字典(Win32)
  • geteuid()/seteuid() 获取/设置当前进程的有效用户ID(UID)
  • getegid()/setegid() 获取/设置当前进程的有效组ID(GID)
  • getpgid(pid)/setpgid(pid,pgrp) 获取/设置PID进程的GID;对于get,如果pid为0,便返回现在进程的GID
  • getlogin() 返回运行现在进程的用户登录
  • times() 返回各种进程时期的元组(Win32)
  • strerror(code) 返回和错误代码对应的错误信息(Win32)
  • getloadavg()(2.3) 返回代表在过去1,5,15分钟内的系统平均负载值的元组

8、相关模块

  • atexit(2.0) 注册当Python解释器退出时的执行句柄
  • popen2 提供额外的在os.popen之上的功能;提供通过标准文件和其他的进程交互的能力
  • commands 提供额外的在os.sysytem之上的功能;把所有的程序输出保存在返回的字符串中(与输出到屏幕的相反)
  • getopt 在这样的应用程序中的处理选项和命令行参数
  • site 处理site-specific模块或包
  • platform(2.3) 底层平台和架构的属性
  • subprocess(2.4)管理(计划替代旧的函数和模块,比如os.system()、os.spawn*()、os.popen*()、popen2.和commands.
作者:tiana0 发表于2016/7/23 22:41:17 原文链接
阅读:32 评论:0 查看评论

实践篇之论机器人的环境感知与智主运动

$
0
0

论机器人的环境感知与智主运动

–兼谈基于微分几何的人工智能

标签(空格分隔): 人工智能 计算机视觉 自主移动 微分流形 Ricci流


版权声明:本文为作者原创文章,未经作者允许不得转载。

前言

人工智能是分主观与客观的,是硬币的两个方面,
客观智能是世界的本质描述,是物理的是数学的,
主观智能是来自于客观智能,是哲学的是宗教的。
抛开物理与数学只讲方法是走不远的,如无本之木、无源之水,
单讲物理与数学只会得到一个静默纷扰的世界,无乐无诗无书无画。

序言

什么是智能,这是一个令人思绪飞扬的问题,本文的内容经过了我的理论计算及实践,其中的方法及算法全部在我的六个发明专利里,既反映了我的想法和兴趣,也暴露了我的局限和偏见,这里要感谢我太太对我研究工作的持续激励,也感谢我可爱的孩子Andie,在这段时间里所带给我的欢乐与慰藉。

加州大学圣塔芭芭拉分校(UCSB)著名的理论和实验物理学家Matthew Fisher就笃信,人的意识、记忆和思维是量子纠缠的,要用量子理论来解释,这也从一个方面说明当前各种xx学习远不足以解释AI的现象,根源在所用数学理论,绕不开梯度散射等局限性,理不顺长短记忆,道不清缘由,指不明方向;最有可能统一物理的微分几何,可做人脸识别表情识别,携带了原始三维信息,其应用前景不可估量;几何决定了光的弯曲与时空弯曲,可做环境识别,可做智主移动,远不是巡线、避障、定位建图的层次;物理将统一于几何,宏微观将统一于几何,主客观将统一于几何,智能在微分几何,莫比乌斯带上的“蚂蚁”将揭开其中奥妙。

第一章 机器对环境的感知

人类无时无刻不在通过眼、耳、鼻、舌等器官来摄取环境的信息,动物也是同样通过各自的器官获得环境信息。而对无生命的机器来说,若要体现出类人的智能,首要就是要考虑如何感知环境的信息。

1.1 机器的感官之传感器

借鉴人与动物的感官,我们创造出了各种各样的传感器,以辅助机器感知周围的环境,部分如下:

  • 重力传感器G-sensor,可以感知静态的重力加速度,游戏设计中多用于感知姿态
  • 陀螺仪Gyroscope,常用来获取运动中的物体的各种姿态
  • 激光测距传感器,可以测量环境物的距离信息,类似的还有红外、超声波等传感器
  • 光线传感器,感知环境光亮的强度
  • 温湿度传感器,测量周围环境的温度与湿度
  • 光学编码器,可以计算电机的转速

1.2 机器的思考与学习特征

机器在通过传感器摄取到环境信息后,它是怎么识别的?它是怎么记忆的?它又是怎么触类旁通的?,说实话,如果真正解决了这些问题,那么也就离发展出通用智能不远了。单就视觉与运动这一方向来说,还是有路可循的。

从人类的视觉出发,一般来讲,人们通过物体的色彩、材质、形状及拓扑信息来辨识与比较物体。对机器来说,色彩可以由camera的感光器件可以识别,材质也有各种传感器做出些许识别,形状可以由基于测距的扫描scanner感知,进而再计算建模得出其表面形状信息,更进一步可以计算其拓扑信息。所以,机器视觉还是可以在一定程度上比肩人类,根据视频感知,实时做出路径规划与自主运动,类似的无人驾驶汽车,已经研制出来,相信不远的将来就可以走向商业应用。

1.3 机器的路径规划与避障

有了前面章节的基础感知信息,机器就可以大规模的计算,基本上可以做到实时的路径规划与避障。当前常见的这方面的算法有A*算法、D*算法等,应用方面,大到火星车上面,小到游戏应用设计里,都能见到其身影。这些路径规划算法,其应用基础就是要有一个环境地图,而这地图的获取方面,同步定位与建图之SLAM算法,名满全行业,而且已经有了初步的商业应用,如扫地机器人,像美国的iRobot公司、中国的科沃斯公司等等,都已经做了较深入的研究与产品的商业化推广。

1.4 蝙蝠的运动特征

蝙蝠的飞行不是靠眼睛看的,众所周知,而是靠耳朵和发音器官嘴与鼻以飞行的。蝙蝠在飞行时,会发出一种尖叫声,这是一种超声波信号,是人类无法听到的,因为它的音频超出人耳能接收到的范围20Hz~20kHz。这些超声波的信号在发出之后几乎呈直线传播,如果在传播时碰到其他物体,就会立刻反射回来,在接收到返回的信息之后,蝙蝠会实时对这些信号进行计算,感知周围环境,并且快速地绕开障碍物,去捕捉适合自己品味的猎物。

喉咙是蝙蝠发出超声波的源泉,嘴和鼻子是其超声波的发射天线,再通过耳朵接收返回的声波。进而分辨出物体是大是小,是远是近,是活是死,从而也就能知道他所需要的食物在什么方向。

科学家把蝙蝠的这一行为叫做回声定位,并且根据这一原理制造出了雷达,这一可以利用无线电波进行探测的装置。时至今日,雷达已经被广泛应用在军事、天文、气象、航海、航空等领域里。

第二章 当前智能算法与应用

现代意义上的人工智能AI(Artificial Intelligence),其诞生标志被广泛承认是1956年达特矛斯会议,人工智能几十年来的发展经历了多次高潮与低谷。截止目前,我们所接触到的智能学习算法包括有以下几大类:模式识别(Pattern recognition)、机器学习(machine learning)、深度学习(deep learning),其中深度学习近年来明显被热捧,以下图所示为来自谷歌趋势的“2004年1月到2016年6月间三种学习热度”,借Tomasz Malisiewicz博士的话说:

模式识别一开始主要是作为机器学习的代名词;
模式识别正在慢慢没落和消亡;
机器学习就像是一个真正的冠军一样持续昂首而上;
深度学习是个崭新的和快速攀升的领域。
2004年1月到2016年6月间三种学习热度,来自谷歌趋势
2004年1月到2016年6月间三种学习热度,来自谷歌趋势

2.1 一种机器学习算法与应用

什么是机器学习,各家定义不尽相同,然而个人喜好Tom Mitchell在《Machine Learning(中文版:计算机科学丛书:机器学习 )》一书中的提法:这门学科所关注的问题是“计算机程序如何随着经验积累自动提高性能”。初学者可以简单认为机器学习分类为监督学习与无监督学习,监督学习多用于解决分类、回归等问题,无监督学习多用于解决聚类、密度估计等问题。我们常见的一种机器学习应用便是手写数字识别,例如邮政编码自动识别系统、税表与银行支票自动识别系统。

2.2 一种深度学习算法与应用

深度学习的灵感源自于人脑视觉系统,在某种程度上,可以认为深度学习是机器学习的一个分支,深度学习的概念源于人工神经网络的研究,在语音识别和图像识别等领域获得了不小的成功,从识别率方面来讲可以说是超过了其他方法,微软将其应用的语音识别中,Google Brain系统用其识别图像。

以下是典型的多层深度学习结构
多隐层深度学习结构
经典的多隐层深度学习结构

第三章 微分几何与人工智能

截止目前,人类的智能是如何运转的仍然是未解。对于未来人工智能的发展,有乐观派,也有悲观派。另一方面,微分几何的技术应用,对人们认识自身的智能及医辽方面,有了不小的进展,可以参考顾险峰老师的文章:纯粹数学的雪崩效应:庞加莱猜想何以造福了精准医疗?

3.1 视觉中枢与微分几何

诺贝尔奖获得者David Hunter Hubel与Torsen Wiesel,通过对猫的视觉中枢研究,证明视觉中枢系统具有由简单模式构成复杂模式的功能,这对人工神经网络的发展有一定的启发。后来,通过对猴子视觉中枢的解剖,发现从视网膜到第一视觉中枢的大脑皮层曲面之间存在共形映射。而,共形映射属于微分几何的范畴,再配以庞加莱猜想的单值化定理,所有现实的物体及环境都可以在微分几何的范围内施以更形象化的运算。参考顾险峰老师的文章:人工智能中的符号主义和联结主义

3.2 微分几何在医学中的应用

近些年来,北美和日本采用了虚拟肠镜技术,用以微分几何的Ricci曲率流方法计算器官的共形映射,从而提高早期直肠癌的发现几率,降低了直肠癌的死亡率。另一应用就是脑神经疾病的预防与诊断,例如癫痫、儿童自闭症等脑神经疾病,利用微分几何的知识就可以将相关器官共形映射到球面上,进而就可以加以精确分析比对。

第四章 机器视觉数字化

机器视觉,类似于尚未被认知的生物视觉,同样是没有彻底的、通用的方法加以实现,截止目前,机器视觉仍是工程领域、科学领域中的一个极富挑战性的重要研究领域,它是一门综合性的学科,包括计算机科学和工程、信号处理、物理学、应用数学和统计学、神经生理学和认知科学等。本文从一个新的角度切入,对机器人的环境感知与自主运动的问题进行了探讨,通过探讨,笔者提出了一些自己的看法,给出了一套视觉感知算法,以期能起到抛砖引玉的效果。

4.1 环境物体的视觉特征

相比于同时定位与建图的SLAM算法与图像处理的SIFT算法,笔者这里给出的视觉感知算法Dog-like AI与其有相同点,也有明显的区别。与SLAM一样要依赖测距传感器扫描做为信息的输入,与SIFT算法一样是基于特征运算的,不同的是,Dog-like AI算法是基于几何拓扑的,数学基础是微分几何,从结构上来讲,由简单到复杂、由局部到整体,一定程度上可以认为比较符合生物的认知原理,人类的视觉中枢本身由多个区域,分层、抽象是其重大特征。

如下图所示的,基于微分几何的等曲率共形变换
基于微分几何的共形变换
图片参考顾险峰老师的文章:纯粹数学的雪崩效应:庞加莱猜想何以造福了精准医疗?

在一个等曲率的几何拓扑空间上,对所有的物体加以分析运算,这有可能是人工智能一种通用模型,既有局部又有整体,既有简单又有复杂,既有低级刻画又有高级抽象,也体现了连续与离散的统一。Dog-like AI算法也是建立在“信息存储与记忆皆在几何结构中”之假设基础上。

4.2 视觉特征的空间结构

在实践中,针对现实生活中的物体或环境,在空间某一点用测距传感器扫描,获取物体表面各点的距离信息,可以将物体表面双射到某一基准面上,即双射,

f:M(x,y,z)S(β,θ)=dS(β,θ,d)
M(x,y,z)表示三维空间中物体表面上的任一点,S(β,θ)表示二维基准面上的任一点,d表示基准面上该点的畸变系数,这里的基准面可以是欧氏平面、球面或双曲平面,但是如果直接这样映射的话会有两个局限点,一是不能保证映射的双射性质,另一是不能确保映射的共形性质,例如当物体表面不是凸性的,此时就会破坏映射的双射性质,就不能统一化到同一个基准面上分析比对,同样道理,如果不是共形到同一个基准面上,也会对分析结果造成错位,相同的两个物体表面双射共形映射到同一个基准面上,变换后的结果之间顶多相差一个莫比乌斯(Mobius)变换。详细的等曲率双射共形变换到基准面的数学知识及算法后续详解。

由上我们得出,实际物体双射共形变换到基准面上后,其对应的点云空间,如下:

f(M)=S(β1,θ1,d11)S(β2,θ1,d21)S(βn,θ1,dn1)S(β1,θ2,d12)S(β2,θ2,d22)S(βn,θ2,dn2)S(β1,θs,d1s)S(β2,θs,d2s)S(βn,θs,dns)
然后,把所有的点S(β,θ,d)按位置(β,θ)离散化成二维矩阵,此二维矩阵称为共形矩阵,如下,其中的元素是由畸变系数d分解而得,
c(M)=f11f21fn1f12f22fn2f1sf2sfns
根据共形矩阵,我们可以求出物体的特征矩阵,如下,
F(M)=φ(c(M))=F11F21Fn1F12F22Fn2F1sF2sFns
其中,φ可以是一种变换,可以是但不限于二维Fourier变换、二维Walsh变换等,从特征矩阵中,我们可以方便地分析物体之间的对称性(镜像对称性、平移不变性及旋转不变性)与差异性,为了更方便地分析物体之间的差异性,可以进而求取特征矩阵的特征谱,如下,
P(M)=Ψ(F(M))
其中,Ψ是一种变换,随后我们举例,单从Walsh变换详解之。

4.2.1 什么是Walsh变换

Walsh变换全称Walsh Hadama变换,是一种离散正交变换,也是一种完备的正交变换方法,它本身也是一种矩阵,记为Hn,n2m,mN

H2n=(HnHnHnHn),H1=(1)
Walsh Hadama矩阵可以作用于一维的数组,也可以作用于二维的矩阵。

4.2.2 Walsh变换应用

根据Walsh Hadama矩阵,我们就可以利用Walsh Hadama变换求出物体的特征矩阵(二维空间)或特征向量(一维空间),如下,

F(M)=φ(c(M))=φf11f21fn1f12f22fn2f1sf2sfns=1nsHnf11f21fn1f12f22fn2f1sf2sfnsHs
有了特征矩阵,我们就可以得出特征谱向量P(M)了,如下,
P(M)=P(M)[r]=(F(M)[0][0])2,P(M)[r]=rowi=0colj=0(F(M)[i][j])2r1k=0P(M)[k]τ=max(n,s),row=min(2r1,n1),col=min(2r1,s1) r=0 r[1,logτ2]
有了特征谱向量,就可以比较方便地分析两个物体的差异性了,例如,可以根据向量余弦法来分析,向量余弦夹角的度数越靠近0,就表明这两个物体越相似,如下,
cos(P(M),P(N))=<P(M),P(N)>|P(M)||P(N)|

当其余弦夹角为零时,我们可以判断两个物体是相似的,可以很容易地求出其相似比,概念上类似真实汽车与汽车模型的区别。

4.2.3 共形双射之等曲率变换

经过以上几小节的分析,我们发现还有一个环节没有处理,就是怎么计算物体的共形双射之等曲率变换。这里我们就不得不提大名顶顶的“庞加莱猜想之单值化定理”。庞加莱(Poincaré)于1904年提出的曲率单值化猜想,现在看来给物体识别带来了一线曙光,可以让具有复杂拓扑的物体微分同胚到正则空间内,这里的正则空间与前述基准面的概念是相通的,可以是欧氏空间、球面空间或双曲空间;上世纪七、八十年代哈密尔顿(Richard Hamilton)的工作、本世纪最初的几年里佩雷尔曼(Grigoriy Perelman)的工作彻底从理论上证明了庞加莱猜想,并且进一步证明此微分同胚是可以保形的,其计算复杂度上来讲是指数级收敛,非常高效。
限于篇幅,我们这里只给出一个可行的大概的示意算法,对于一个三维闭合曲面(或是有缺口的)表示的物体,通过相关设备及算法,例如三维激光雷达、双目摄像头等,已经获取了实际环境物体的三维点云信息,其包括以下步骤:
1. 由物体的点云信息构建物体的三维栅格地图时,对点云数据做中间处理,这些中间处理环节包括但不限于插值优化、平滑优化、扫描点过滤等,
2. 由物体的三维栅格地图,根据Delaunay算法或其他算法构建物体的三角剖分网格图,
3. 计算物体的三角剖分网格图的欧拉示性数,并确定共形变换时单值化的曲率及所嵌背景几何,
4. 由Circle Packing、离散Ricci流方法,迭代计算,把物体的三角剖分网格图共形映射到常曲率的标准正则空间内:球面几何、欧氏几何、双曲几何,
5. 根据曲率及度量等信息,把物体表面的所有的点,拉回嵌入到标准正则空间内,在正则空间内的点,如前4.2节所述表示为S(β,θ,d)
6. 接前4.2节所述,我们就可以求出相应的特征矩阵与特征谱。

这里对Circle Packing、离散Ricci流方法不再展开,详细可以参考相关书箱与论文。
Circle Packingy方法如下图示,
Circle Packingy方法
基于Delaunay三角剖分的离散Ricci流图示,
基于Delaunay三角剖分的离散Ricci流

4.3 视觉矩阵的对称性

接上前面章节分析,两个物体相似时,如果相似比为1,我们可以称此两个物体是等价的,从特征矩阵出发,在具有相似关系下的两个物体,转化为等价关系后,就可以分析其镜像对称性、平移不变性及旋转不变性。

4.3.1 相似性

由特征谱P(M)P(N),判断出物体MN相似后,就可以由特征谱来计算其相似比,如下:

P(N)=IλP(M)=λ100λnP(M)
考虑到i[1,n],λi有等于零的情况,我们取
Iλ=λ100λm,mn,j[1,m],λj>0
令,
Iλ=tr(Iλ)m=tr(Iλ)m=1mj=1mλj
,则我们定义物体MN的相似比为λP(M)P(N)

4.3.2 不变性

由特征谱P(M)P(N),判断出物体MN相似后,就可以由特征矩阵F(M)F(N)来计算其镜像对称性,首先把物体MN转化为等价关系,即,

F(M)λF(M)
我们以8维特征方阵为例加以说明,取,
8=123,1=(1001),2=(0110),3=0.51111111111111111

D=diag{1,1,1,1,1,1,1,1}
,记
{F(N)=(8)uDpF(M)Dq(8)vu,v[0,1,2,...,7],p,q(0,1)
当满足以下条件时,分别有相应的镜像对称性与平移不变性,
p=1q=1u>0v>0F(M)F(N)F(M)F(N)F(M)F(N)F(M)F(N)
,记
{F(N)=(8)uDpF(M)Dq(8)vu,v[0,1,2,...,7],p,q(0,1)
当满足以下条件时,分别有相应的旋转不变性,
{p=1,q=0p=0,q=1F(M)F(N)π2F(M)F(N)π2

4.3.3 不变性样例

如下所示,假设我们已经得到一个物体的

第五章 机器视觉

5.1 二维环境感知

5.2 三维环境感知

5.3 智主运动

附录

A Fourier变换

B 离散正交变换

C 复变函数

参考:微分几何与黎曼几何1,还有部分源程序代码2


作者 hjwang1@163.com
2016 年 05月 20日


  1. 《微分几何讲义》,陈省身、陈维桓著,访问 Math 参考更多内容。
  2. 代码参见,github
作者:hjwang1 发表于2016/7/23 22:47:33 原文链接
阅读:19 评论:0 查看评论

Java 集合框架(一)_day15

$
0
0

集合框架(对象数组的概述和使用)

  • A:案例演示
    • 需求:我有5个学生,请把这个5个学生的信息存储到数组中,并遍历数组,获取得到每一个学生信息。
  • Student[] arr = new Student[5];                 //存储学生对象
    arr[0] = new Student("张三", 23);
    arr[1] = new Student("李四", 24);
    arr[2] = new Student("王五", 25);
    arr[3] = new Student("赵六", 26);
    arr[4] = new Student("马哥", 20); 
    
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
    }
    
  • B:画图演示

    • 把学生数组的案例画图讲解这里写图片描述
    • 数组和集合存储引用数据类型,存的都是地址值

集合框架(集合的由来及集合继承体系图)

  • A:集合的由来
    • 数组长度是固定,当添加的元素超过了数组的长度时需要对数组重新定义,太麻烦,java内部给我们提供了集合类,能存储任意对象,长度是可以改变的,随着元素的增加而增加,随着元素的减少而减少
  • B:数组和集合的区别
    • 区别1 :
      • 数组既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值
      • 集合只能存储引用数据类型(对象)集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象
    • 区别2:
      • 数组长度是固定的,不能自动增长
      • 集合的长度的是可变的,可以根据元素的增加而增长
  • C:数组和集合什么时候用
    * 1,如果元素个数是固定的推荐用数组
    * 2,如果元素个数不是固定的推荐用集合
  • D:集合继承体系图
  • 这里写图片描述

集合框架(Collection集合的基本功能测试)

  • A:案例演示

  • *
 public class Demo2_Collection {

    /**
     * * A:案例演示 
        * 
                基本功能演示

                boolean add(E e)
                boolean remove(Object o)
                void clear()
                boolean contains(Object o)
                boolean isEmpty()
                int size()

        * B:注意:
        * 
                collectionXxx.java使用了未经检查或不安全的操作.
                注意:要了解详细信息,请使用 -Xlint:unchecked重新编译.
                java编译器认为该程序存在安全隐患
                温馨提示:这不是编译失败,所以先不用理会,等学了泛型你就知道了
        add方法如果是List集合一直都返回true,因为List集合中是可以存储重复元素的
        如果是Set集合当存储重复元素的时候,就会返回false

        ArrayList的父类的父类重写toString方法,所以在打印对象的引用的时候,输出的结果不是Object类中toString的结果
     */
    public static void main(String[] args) {
        //demo1();
        Collection c = new ArrayList();     
        c.add("a");
        c.add("b");
        c.add("c");
        c.add("d");

        //c.remove("b");                                        //删除指定元素
        //c.clear();                                            //清空集合
        //System.out.println(c.contains("b"));                  //判断是否包含
        //System.out.println(c.isEmpty());
        System.out.println(c.size());                           //获取元素的个数
        System.out.println(c);
    }

    public static void demo1() {
        Collection c = new ArrayList();                     //父类引用指向子类对象
        boolean b1 = c.add("abc");
        boolean b2 = c.add(true);                           //自动装箱new Boolean(true);
        boolean b3 = c.add(100);
        boolean b4 = c.add(new Student("张三",23));           
        boolean b5 = c.add("abc");

        System.out.println(b1);
        System.out.println(b2);
        System.out.println(b3);
        System.out.println(b4);
        System.out.println(b5);

        System.out.println(c.toString());
    }

}

集合框架(集合的遍历之集合转数组遍历)

  • A:集合的遍历
    • 其实就是依次获取集合中的每一个元素。
  • B:案例演示

    • 把集合转成数组,可以实现集合的遍历
    • toArray()
      *

      Collection coll = new ArrayList();
      coll.add(new Student("张三",23));     //Object obj = new Student("张三",23);
      coll.add(new Student("李四",24));
      coll.add(new Student("王五",25));
      coll.add(new Student("赵六",26));
      
      Object[] arr = coll.toArray();              //将集合转换成数组
      for (int i = 0; i < arr.length; i++) {
          Student s = (Student)arr[i];            //强转成Student
          System.out.println(s.getName() + "," + s.getAge());
      }
      

15.集合框架(Collection集合的带All功能测试)

  • A:案例演示
public class Demo4_CollectionAll {

    /**
     * * A:案例演示
        * 
                带All的功能演示
                boolean addAll(Collection c)
                boolean removeAll(Collection c)
                boolean containsAll(Collection c)
                boolean retainAll(Collection c)
     */
    public static void main(String[] args) {
        //demo1();
        //demo2();
        //demo3();
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("c");
        c2.add("d");
        c2.add("e");
        c2.add("f");

        //取交集,如果调用的集合改变就返回true,如果调用的集合不变就返回false
        boolean b = c1.retainAll(c2);                   //取交集
        System.out.println(b);
        System.out.println(c1);
    }

    public static void demo3() {
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("z");

        boolean b = c1.containsAll(c2);             //判断调用的集合是否包含传入的集合
        System.out.println(b);
    }

    public static void demo2() {
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        Collection c2 = new ArrayList();
        c2.add("a");
        c2.add("b");
        c2.add("z");

        boolean b = c1.removeAll(c2);                   //删除的是交集
        System.out.println(b);
        System.out.println(c1);
    }

    public static void demo1() {
        Collection c1 = new ArrayList();
        c1.add("a");
        c1.add("b");
        c1.add("c");
        c1.add("d");

        Collection c2 = new ArrayList();            //alt + shift + r改名
        c2.add("a");
        c2.add("b");
        c2.add("c");
        c2.add("d");

        //c1.addAll(c2);                            //将c2中的每一个元素添加到c1中
        c1.add(c2);                                 //将c2看成一个对象添加到c1中
        System.out.println(c1);
    }

15.集合框架(集合的遍历之迭代器遍历)

  • A:迭代器概述
    • 集合是用来存储元素,存储的元素需要查看,那么就需要迭代(遍历)
  • B:案例演示

    • 迭代器的使用

      Collection c = new ArrayList();
      c.add("a");
      c.add("b");
      c.add("c");
      c.add("d");
      
      Iterator it = c.iterator();                     //获取迭代器的引用
      while(it.hasNext()) {                           //集合中的迭代方法(遍历)
          System.out.println(it.next());
      }
      

15.集合框架(Collection存储自定义对象并遍历)

  • A:案例演示

    • Collection存储自定义对象并用迭代器遍历
    • Collection c = new ArrayList();
      
      c.add(new Student("张三",23));
      c.add(new Student("李四",24));
      c.add(new Student("王五",25));
      c.add(new Student("赵六",26));
      c.add(new Student("赵六",26));
      
      for(Iterator it = c.iterator();it.hasNext();) {
          Student s = (Student)it.next();                     //向下转型
          System.out.println(s.getName() + "," + s.getAge()); //获取对象中的姓名和年龄
      }
      System.out.println("------------------------------");
      Iterator it = c.iterator();                             //获取迭代器
      while(it.hasNext()) {                                   //判断集合中是否有元素
          //System.out.println(((Student)(it.next())).getName() + "," + ((Student)(it.next())).getAge());
          Student s = (Student)it.next();                     //向下转型
          System.out.println(s.getName() + "," + s.getAge()); //获取对象中的姓名和年龄
      }
      

15.集合框架(迭代器的原理及源码解析)(了解)

  • A:迭代器原理
    • 迭代器原理:迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样,那么就需要在每一个类中定义hasNext()和next()方法,这样做是可以的,但是会让整个集合体系过于臃肿,迭代器是将这样的方法向上抽取出接口,然后在每个类的内部,定义自己迭代方式,这样做的好处有二,第一规定了整个集合体系的遍历方式都是hasNext()和next()方法,第二,代码有底层内部实现,使用者不用管怎么实现的,会用即可
  • B:迭代器源码解析
    • 1,在eclipse中ctrl + shift + t找到ArrayList类
    • 2,ctrl+o查找iterator()方法
    • 3,查看返回值类型是new Itr(),说明Itr这个类实现Iterator接口
    • 4,查找Itr这个内部类,发现重写了Iterator中的所有抽象方法

15.集合框架(List集合的特有功能概述和测试)

* public class Demo1_List {

    /**
     *  * void add(int index,E element)
        * E remove(int index)
        * E get(int index)
        * E set(int index,E element)
     */
    public static void main(String[] args) {
        //demo1();
        //demo2();
        //demo3();
        //demo4();
        List list = new ArrayList();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");

        list.set(1, "z");                       //将指定位置的元素修改
        System.out.println(list);
    }

    public static void demo4() {
        List list = new ArrayList();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");

        //Object obj1 = list.get(2);
        //System.out.println(obj1);
        //通过索引遍历List集合
        for(int i = 0;i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }

    public static void demo3() {
        List list = new ArrayList();
        list.add(111);
        list.add(222);
        list.add(333);

        list.remove(111);                           //删除的时候不会自动装箱,把111当作索引
        System.out.println(list);
    }

    public static void demo2() {
        List list = new ArrayList();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");

        Object obj = list.remove(1);                //通过索引删除元素,将被删除的元素返回
        System.out.println(obj);
        System.out.println(list);
    }

    public static void demo1() {
        List list = new ArrayList();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add(4, "f");                       //index<=size并且index>=0都不会报异常
        //list.add(1,"e");
        //list.add(10, "z");                    //java.lang.IndexOutOfBoundsException,当存储时使用不存在的索引时
        System.out.println(list);
    }

}

15.集合框架(List集合存储学生对象并遍历)

  • A:案例演示

    • 通过size()和get()方法结合使用遍历。
    • List list = new ArrayList();
      list.add(new Student("张三", 18));
      list.add(new Student("李四", 18));
      list.add(new Student("王五", 18));
      list.add(new Student("赵六", 18));
      
      for(int i = 0; i < list.size(); i++) {
          Student s = (Student)list.get(i);
          System.out.println(s.getName() + "," + s.getAge());
      }
      

15.集合框架(并发修改异常产生的原因及解决方案)

  • A:案例演示

    • 需求:我有一个集合,请问,我想判断里面有没有”world”这个元素,如果有,我就添加一个”javaee”元素,请写代码实现。

      List list = new ArrayList();
      list.add("a");
      list.add("b");
      list.add("world");
      list.add("d");
      list.add("e");
      
      /*Iterator it = list.iterator();
      while(it.hasNext()) {
          String str = (String)it.next();
          if(str.equals("world")) {
              list.add("javaee");         //这里会抛出ConcurrentModificationException并发修改异常
          }
      }*/
      
  • B:ConcurrentModificationException出现

    • 迭代器遍历,集合修改集合
  • C:解决方案

    • a:迭代器迭代元素,迭代器修改元素(ListIterator的特有功能add)
    • b:集合遍历元素,集合修改元素

      ListIterator lit = list.listIterator();     //如果想在遍历的过程中添加元素,可以用ListIterator中的add方法
      while(lit.hasNext()) {
          String str = (String)lit.next();
          if(str.equals("world")) {
              lit.add("javaee");  
              //list.add("javaee");
          }
      }
      

15.12_集合框架(ListIterator)(了解)

  • boolean hasNext()是否有下一个
  • boolean hasPrevious()是否有前一个
  • Object next()返回下一个元素
  • Object previous();返回上一个元素
  • public class Demo4_ListIterator {
/**
     * @param args
     */
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("a");                                  //Object obj = new String();
        list.add("b");
        list.add("world");
        list.add("c");
        list.add("d");
        list.add("e");

        ListIterator lit = list.listIterator();         //获取迭代器
        while(lit.hasNext()) {
            System.out.println(lit.next());             //获取元素并将指针向后移动
        }

        System.out.println("-----------------");

        while(lit.hasPrevious()) {
            System.out.println(lit.previous());         //获取元素并将指针向前移动
        }
    }

}

15.集合框架(Vector的特有功能)

  • A:Vector类概述
  • B:Vector类特有功能
    • public void addElement(E obj)
    • public E elementAt(int index)
    • public Enumeration elements()
  • C:案例演示

    • Vector的迭代
      Vector v = new Vector(); //创建集合对象,List的子类
      v.addElement(“a”);
      v.addElement(“b”);
      v.addElement(“c”);
      v.addElement(“d”);

      //Vector迭代
      Enumeration en = v.elements();          //获取枚举
      while(en.hasMoreElements()) {           //判断集合中是否有元素
          System.out.println(en.nextElement());//获取集合中的元素
      }
      

15.14_集合框架(数据结构之数组和链表)

  • A:数组
    • 查询快修改也快
    • 增删慢
  • B:链表
    • 查询慢,修改也慢
    • 增删快

15.15_集合框架(List的三个子类的特点)

  • A:List的三个子类的特点
  • ArrayList:
        底层数据结构是数组,查询快,增删慢。
        线程不安全,效率高。
    Vector:
        底层数据结构是数组,查询快,增删慢。
        线程安全,效率低。
    Vector相对ArrayList查询慢(线程安全的)
    Vector相对LinkedList增删慢(数组结构)
    LinkedList:
        底层数据结构是链表,查询慢,增删快。
        线程不安全,效率高。
    
    Vector和ArrayList的区别
        Vector是线程安全的,效率低
        ArrayList是线程不安全的,效率高
    共同点:都是数组实现的
    ArrayList和LinkedList的区别
        ArrayList底层是数组结果,查询和修改快
        LinkedList底层是链表结构的,增和删比较快,查询和修改比较慢
    共同点:都是线程不安全的
    
  • B:List有三个儿子,我们到底使用谁呢?
    查询多用ArrayList
    增删多用LinkedList
    如果都多ArrayList
作者:qq_16103331 发表于2016/7/23 22:50:27 原文链接
阅读:23 评论:0 查看评论
Viewing all 35570 articles
Browse latest View live


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