icon

新闻资讯

News and information

  • 21

    02/01

    PHP环境安全加固方案

    PHP应用部署后,开发者或者运维人员应该时刻关注PHP方面的漏洞消息,升级PHP版本,对PHP环境进行安全加固。本文将给大家介绍如何从WEB安全方面让你的网站更坚固更安全。1.启用PHP的安全模式PHP环境提供的安全模式是一个非常重要的内嵌安全机制,PHP安全模式能有效控制一些PHP环境中的函数(例如system()函数),对大部分的文件操作函数进行权限控制,同时不允许对某些关键文件进行修改(例如/etc/passwd)。但是,默认的php.ini配置文件并没有启用安全模式。您可以通过修改php.ini配置文件启用PHP安全模式:safe_mode=on2.用户组安全当您启用安全模式后,如果safe_mode_gid选项被关闭,PHP脚本能够对文件进行访问,且相同用户组的用户也能够对该文件进行访问。因此,建议您将该选项设置为关闭状态:safe_mode_gid=off注意:该选项参数仅适用于Linux操作系统。如果不进行该设置,您可能无法对服务器网站目录下的文件进行操作。3.安全模式下执行程序主目录如果启用了安全模式后,想要执行某些程序的时候,可以指定需要执行程序的主目录,例如:safe_mode_exec_dir=/usr/bin一般情况下,如果不需要执行什么程序,建议您不要指定执行系统程序的目录。您可以指定一个目录,然后把需要执行的程序拷贝到这个目录即可,例如:safe_mode_exec_dir=/temp/cmd但是,更推荐您不要执行任何程序。这种情况下,只需要将执行目录指向网页目录即可:safe_mode_exec_dir=/usr/www注意:执行目录的路径以您实际操作系统目录路径为准。4.安全模式下包含文件如果您需要在安全模式下包含某些公共文件,您只需要修改以下选项即可:safe_mode_include_dir=/usr/www/include/一般情况下,PHP脚本中包含的文件都是在程序已经写好的,可以根据您的具体需要进行设置。5.控制PHP脚本能访问的目录使用open_basedir选项能够控制PHP脚本只能访问指定的目录,这样能够避免PHP脚本访问不应该访问的文件,一定程度下降低了phpshell的危害。一般情况下,可以设置为只能访问网站目录:open_basedir=/usr/www6.关闭危险函数如果您启用了安全模式,那么可以不需要设置函数禁止,但为了安全考虑,还是建议您进行相关设置。例如,您不希望执行包括system()等在内的执行命令的PHP函数,以及能够查看PHP信息的phpinfo()等函数,那么您可以通过以下设置禁止这些函数:disable_functions=system,passthru,exec,shell_exec,popen,phpinfo,escapeshellarg,escapeshellcmd,proc_close,proc_open,dl如果您想要禁止对于任何文件和目录的操作,那么您可以关闭以下文件相关操作。disable_functions=chdir,chroot,dir,getcwd,opendir,readdir,scandir,fopen,unlink,delete,copy,mkdir,rmdir,rename,file,file_get_contents,fputs,fwrite,chgrp,chmod,chown注意:以上设置中只列举了部分比较常用的文件处理函数,您也可以将上面的执行命令函数和这些文件处理函数相结合,就能给抵制大部分的phpshell威胁。7.关闭PHP版本信息在HTTP头中的泄露为了防止黑客获取服务器中PHP版本的信息,您可以禁止该信息在HTTP头部内容中泄露:expose_php=off这样设置之后,黑客在执行telnet<domain>80尝试连接您的服务器的时候,将无法看到PHP的版本信息。8.关闭注册全局变量在PHP环境中提交的变量,包括使用POST或者GET命令提交的变量,都将自动注册为全局变量,能够被直接访问。这对您的服务器是非常不安全的,因此建议您将注册全局变量的选项关闭,禁止将所提交的变量注册为全局变量。register_globals=off注意:该选项参数在PHP5.3以后的版本中已被移除。当然,如果这样设置之后,获取对应变量的时候就需要采取合理方式。例如,获取GET命令提交的变量var,就需要使用$_GET['var']命令来进行获取,在进行PHP程序设计时需要注意。9.SQL注入防护SQL注入是一个非常危险的问题,小则造成网站后台被入侵,重则导致整个服务器沦陷。magic_quotes_gpc选项默认是关闭的。如果打开该选项,PHP将自动把用户提交对SQL查询的请求进行转换(例如,把’转换为\’等),这对于防止SQL注入攻击有很大作用,因此建议您将该选项设置为:magic_quotes_gpc=on注意:该选项参数在PHP5.4.0以后的版本中已被移除。所以最好使用PDO预处理方式处理SQL查询。10.错误信息控制一般PHP环境在没有连接到数据库或者其他情况下会有错误提示信息,错误信息中可能包含PHP脚本当前的路径信息或者查询的SQL语句等信息,这类信息如果暴露给黑客是不安全的,因此建议您禁止该错误提示:display_errors=Off如果您确实要显示错误信息,一定要设置显示错误信息的级别。例如,只显示警告以上的错误信息:error_reporting=E_WARNING&E_ERROR注意:强烈建议您关闭错误提示信息。11.错误日志建议您在关闭错误提示信息后,对于错误信息进行记录,便于排查服务器运行异常的原因:log_errors=On同时,需要设置错误日志存放的目录,建议您将PHP错误日志与Apache的日志存放在同一目录下:error_log=/usr/local/apache2/logs/php_error.log注意:该文件必须设置允许Apache用户或用户组具有写的权限。还有最重要的是升级系统补丁,升级PHP版本。本文摘自阿里云,本站编辑对原文稍作删减。

  • 21

    01/25

    前端Javascript下载文件

    项目开发中经常会有导出数据到Excel类似的需求,或者是下载文档的需求。最简单的下载方式是直接请求服务端文件地址,通过浏览器http实现文件下载。但是开发中,由于项目需求,你要下载的文件地址不会暴露给用户,而且需要鉴权才允许下载文件,这个时候我们该怎么处理呢?应用场景文件地址没有暴露在公网,无法通过文件url直接下载文件。要下载的文件内容可能是根据用户请求动态生成的,如导出Excel数据表。后端需要鉴权验证用户提交的下载请求。实现流程前端发送get/post请求,携带header信息(如token用于鉴权),后端接收请求,完成鉴权后,读取对应的文件,将文件以文件流的形式发送给浏览器,浏览器完成下载。我们把这种方式叫做Blob方式下载。Blob对象表示一个不可变、原始数据的类文件对象。Blob表示的不一定是JavaScript原生格式的数据。前端代码我们以下载图片为例,使用axios来做前端异步下载请求。get方式请求中,我们要在header中携带token信息,这个token就是你在系统中的通行证,一般是你在登录的时候后端给你的token,相当于一张游乐场的门票,有了这张门票,你可以到游乐场里游玩任意项目,只是在游玩时给工作人员出示这个token门票就可以了。并且还要告诉后端,需要后端返回blob类型的数据,使用responseType:'blob'。axios.get('http://localhost:9998/download.php',{headers:{'token':'1234512345'},responseType:'blob'}).then((res)=>{if(res.data.type!=='application/octet-stream'){alert('下载失败');returnfalse;}constblob=newBlob([res.data],{type:'image/jpeg'})leta=document.createElement("a");letobjUrl=URL.createObjectURL(blob);a.href="objUrl;a.download='abc.jpg'//文件名a.click();URL.revokeObjectURL(objUrl);//释放内存document.body.removeChild(a);alert('下载成功');}).catch((err)=>{console.log(err);alert('下载失败');});我们拿到后端返回的blob对象数据后,在页面上创建一个a标签,然后模拟点击事件,将blob数据保存成文件。注意URL.createObjectURL(blob)将blob保存在内存中,下载完后记得释放内存哦。后端代码在后端download.php中,先要在header中允许接收token,如果是跨域请求那就应该还要设置header("Access-Control-Allow-Origin:*");允许跨域请求。header("Access-Control-Allow-Headers:Content-Type,Authorization,X-Requested-With,token");$token=isset($_SERVER['HTTP_TOKEN'])?$_SERVER['HTTP_TOKEN']:'';if($token!='1234512345'){echo'error.';exit;}$file='../file/cc.jpg';if(!file_exists($file)){header('HTTP/1.1404NotFound');exit;}$fileSize=filesize($file);//下载文件需要用到的头header("Content-type:application/octet-stream");header("Accept-Ranges:bytes");header("Accept-Length:".$fileSize);$fp=fopen($file,"rb");$buffer=1024;$fileCount=0;//向浏览器发送数据while(!feof($fp)&&$fileCount<$fileSize){$cont=fread($fp,$buffer);$fileCount =$buffer;echo$cont;}fclose($fp);接着就是验证token是否正确,上述代码中的验证过程是伪代码,实际开发中应该根据业务需求,按照一定的算法验证token。token里面可能含有用户信息和过期时间等数据。然后判断要下载的文件是否存在,这个文件可能不在web目录下,用户无法直接通过url访问。最后就是读取文件流,发送给浏览器。"

  • 21

    01/18

    PHP运行模式:CGI,Fast-CGI,PHP-FPM,PHP-Cli

    PHP有多种运行模式,常见的Fast-CGI,PHP-FPM模式我们归纳为传统的web模式,还有一种模式属于命令行模式:PHP-Cli。他们之间有着怎么样的区别,看本文就够了。CGI协议模式CGI模式是指通用网关接口(CommonGatewayInterface),它允许web服务器通过特定的协议与应用程序通信,调用原理大概为:用户请求->Web服务器接收请求->fork子进程调用程序/执行程序->程序返回内容/程序调用结束->web服务器接收内容->返回给用户。由于每次用户请求,都得fork创建进程调用一次程序,然后销毁进程,所以性能较低。Fast-CGI协议模式Fast-CGI是CGI模式的升级版,它像是一个常驻型的cgi,只要开启后,就可一直处理请求,不再需要结束进程,调用原理大概为:web服务器fast-cgi进程管理器初始化->预先forkn个进程用户请求->web服务器接收请求->交给fast-cgi进程管理器->fast-cgi进程管理区接收,给其中一个空闲fast-cgi进程处理->处理完成,fast-cgi进程变为空闲状态,等待下次请求->web服务器接收内容->返回给用户。注意,Fast-CGI和CGI都是一种协议,开启的进程是单独实现该协议的进程。PHP-FPM模式PHP-FPM(FastCGI进程管理器)用于替换PHPFastCGI的大部分附加功能,对于高负载网站是非常有用的。它的功能包括:支持平滑停止/启动的高级进程管理功能;可以工作于不同的uid/gid/chroot环境下,并监听不同的端口和使用不同的php.ini配置文件(可取代safe_mode的设置);stdout和stderr日志记录;在发生意外情况的时候能够重新启动并缓存被破坏的opcode;文件上传优化支持;"慢日志"-记录脚本(不仅记录文件名,还记录PHPbacktrace信息,可以使用ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢;fastcgi_finish_request()-特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等);动态/静态子进程产生;基本SAPI运行状态信息(类似Apache的mod_status);基于php.ini的配置文件。PHP-FPM的工作原理大概为:php-fpm启动->生成n个fast-cgi协议处理进程->监听一个端口等待任务用户请求->web服务器接收请求->请求转发给php-fpm->php-fpm交给一个空闲进程处理->进程处理完成->php-fpm返回给web服务器->web服务器接收数据->返回给用户。PHP-Cli模式php-cli模式属于命令行模式,对于很多刚开始学php就开始wamp,wnmp的开发者来说是最陌生的一种运行模式。该模式不需要借助其他程序,直接在控制台输入phpxx.php就能执行php代码。命令行模式和常规传统的web模式明显不一样的是:没有超时时间默认关闭buffer缓冲STDIN和STDOUT标准输入/输出/错误的使用echovar_dump,phpinfo等输出直接输出到控制台可使用的类/函数不同php.ini配置的不同在php-cli中,是没有超时时间的,也无法通过set_time_limit设置超时时间,举个栗子:<?phpset_time_limit(30);while(1){echo1;sleep(1);}这段代码,在常规web下运行,只要到30秒就会报Fatalerror:Maximumexecutiontimeof30secondsexceededin......这样的错误。而在php-cli中,这段代码将会一直执行,一直输出1到控制台中。php有些扩展在常规web下运行时没用/没有意义的,例如:swoole扩展socket扩展模块模式Apache+PHP运行时,默认使用的是模块模式,它把php作为apache的模块随apache启动而启动,接收到用户请求时则直接通过调用mod_php模块进行处理,详细内容可自行百度。

  • 20

    08/29

    Linux获取监听指定端口的进程PID

    在Linux下经常需要杀死(重启)监听某端口的进程,因此就写了一个小脚本,通过ss命令获取监听制定端口的进程PID,然后通过kill命令结束掉进程:#!/bin/sh#set-x[[$#-lt1]]&&{echo'paramerror:musthaveoneparam(port)';exit-1;}[[$#-gt1]]&&{echo'paramerror:onlysupportoneparam(port)';exit-1;}functionget_pid_by_listen_port(){pattern_str="*:$1\\b"pid=$(ss-n-t-l-p|grep"$pattern_str"|column-t|awk-F',''{print$(NF-1)}')#当版本号为"ssutility,iproute2-ss161009"时,ss命令输出格式为:#LISTEN05*:8000*:*users:(("python2.7",pid=7130,fd=3))#此时需要进一步处理,只获取进程PID值.[[$pid=~"pid"]]&&pid=$(echo$pid|awk-F'=''{print$NF}')echo$pid}pid=$(get_pid_by_listen_port$1)if[-n"$pid"]thenecho"findpid:$pid,killit..."kill$pidelseecho'cannotfindlistenedport:'$1exit-1fi如果只是想放入.bashrc或.zshrc的话,可以使用下面这个版本:functionkill_pid_by_listen_port(){[[$#-lt1]]&&{echo'paramerror:musthaveoneparam(port)';return-1;}[[$#-gt1]]&&{echo'paramerror:onlysupportoneparam(port)';return-1;}pattern_str="*:$1\\b"pid=$(ss-n-t-l-p|grep"$pattern_str"|column-t|awk-F',''{print$(NF-1)}')#当版本号为"ssutility,iproute2-ss161009"时,ss命令输出格式为:#LISTEN05*:8000*:*users:(("python2.7",pid=7130,fd=3))#此时需要进一步处理,只获取进程PID值.[[$pid=~"pid"]]&&pid=$(echo$pid|awk-F'=''{print$NF}')[[-n"$pid"]]&&{"findpid:$pid,killit..."}[[-n"$pid"]]||{echo"notfoundlistenedport:$1"}}

  • 20

    08/29

    linux下使用cat grep快速查找内容

    查看日志快速定位ERRORcatlog.txt|grep'ERROR'-A5意思是,在log.txt文件中,查找ERROR字符,并显示ERROR所在行的之后5行catlog.txt|grep'ERROR'-B5之前5行catlog.txt|grep'ERROR'-C5前后5行catlog.txt|grep-v'ERROR'排除ERROR所在的行--------------------------------------使用正则表达式来根据时间获取日志//\''中间是有一个空格,这个表示查询10点11分到10点18分数据2019-07-15\10:1[1-8]//同理这个是10点到19点2019-07-15\1[0-9]://同理这个是10点到23点日志2019-07-15\1[0-9]|2019-07-15\2[0-3]样例数据:2019-07-1510:16:45.110|http-nio-8080-exec-11|INFO|2019-07-1510:16:45.112|http-nio-8080-exec-11|INFO|2019-07-1510:17:00.101|orderPayTimeoutTask-0-exe-0|INFO|c2019-07-1510:18:00.101|orderPayTimeoutTask-0-exe-0|INFO|2019-07-1510:19:00.101|orderPayTimeoutTask-0-exe-0|INFO|2019-07-1511:19:20.956|http-nio-8080-exec-50|INFO|2019-07-1512:19:20.956|http-nio-8080-exec-50|INFO|2019-07-1510:20:20.959|http-nio-8080-exec-50|INFO|2019-07-1510:19:34.548|http-nio-8080-exec-33|INFO|2019-07-1510:19:34.548|http-nio-8080-exec-33|INFO|2019-07-1510:19:34.551|http-nio-8080-exec-33|INFO|2019-07-1510:19:35.518|http-nio-8080-exec-89|INFO|2019-07-1510:19:35.518|http-nio-8080-exec-89|INFO|2019-07-1510:19:35.521|http-nio-8080-exec-89|INFO|2019-07-1510:19:38.507|http-nio-8080-exec-77|INFO|2019-07-1510:19:38.507|http-nio-8080-exec-77|INFO|2019-07-1510:19:38.510|http-nio-8080-exec-77|INFO|2019-07-1510:19:52.078|http-nio-8080-exec-90|INFO|2019-07-1510:19:52.078|http-nio-8080-exec-90|INFO|2019-07-1510:19:52.081|http-nio-8080-exec-90|INFO|注:使用“或”需要加-e标识catapp.log|grep-E"2019-07-15\1[0-9]|2019-07-15\2[0-3]"

  • 20

    08/21

    这五种态度正在大肆破坏你的软件开发工作

    细节决定成败,态度决定一切。那些影响最终结果的,往往归根结底在于你不甚在意的事情。即使只是小小的想法

  • 20

    08/07

    js实现ajax局部刷新

    //使用js进行ajax进行提交,异步提交,局部刷新页面//1.创建ajax对象varajax=newXMLHttpRequest();/***2.准备提交数据*参数讲解*1.提价方式"post","get"*2.提交路径"CheckNameServlet?name="+?*3.是否是异步,默认true,可以不写,数据类型为boolean类型*/ajax.open("post","CheckNameServlet?name"+user)//3.提交数据ajax.send();//4.接收Serlvet返回的数据(数据回调)ajax.onreadystatechange=function(){/***判断两个状态(后台已经处理完毕,接收的参数)*1.ajax.readyState*0没有调用send方法*1已经调用send方法,正在发送请求*2send方法已经完成*3正在解析响应内容*4响应内容解析完成*2.ajax.status*https://tool.oschina.net/commons?type=5*/if(ajax.readyState==4&&ajax.status==200){//响应成功,从后台获取数据varmessage=ajax.responseText;usrInfo.innerHTML=message;}

  • 20

    08/05

    解决Redis错误MISCONF Redis is configured to save RDB snapshots

    前言在redis中添加list,字符串类型的键值对之后,redisDeskManager上提示了一些错误信息:MISCONFRedisisconfiguredtosaveRDBsnapshots,butiscurrentlynotabletopersistondisk.Commandsthatmaymodifythedatasetaredisabled.PleasecheckRedislogsfordetailsabouttheerror.翻译为中文大概意思是:Redis被配置为保存数据库快照,但它目前不能持久化到磁盘。用来修改集合数据的命令不能用。请查看Redis日志的详细日志信息。原因强制关闭Redis快照导致不能持久化。解决方法1、将stop-writes-on-bgsave-error值设置为no即可避免这种问题。如果redis缓存的是非重要数据,对可用性要求不高,可以采用这种方式。有两种修改方式:命令行修改方式127.0.0.1:6379>configsetstop-writes-on-bgsave-errorno1修改redis.conf配置文件,更改后需要重启redisvim打开redis.conf文件,使用快捷键匹配模式/stop-writes-on-bgsave-error定位到stop-writes-on-bgsave-error字符串所在位置,将原先设置的yes设置为no。然后重启redis-cli-h127.0.0.1-p6379shutdown2、配置优化,添加以下配置项到/etc/sysctl.conf配置文件vm.overcommit_memory=11执行以下命令使其实时生效:sysctlvm.overcommit_memory=11Linux内核会根据参数vm.overcommit_memory参数的设置决定是否放行。vm.overcommit_memory=1:直接放行vm.overcommit_memory=0:则比较此次请求分配的虚拟内存大小和系统当前空闲的物理内存加上swap,决定是否放行。vm.overcommit_memory=2:则会比较进程所有已分配的虚拟内存加上此次请求分配的虚拟内存和系统当前的空闲物理内存加上swap,决定是否放行。vm.overcommit_memory这个参数又是干什么的呢?Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存,将这些不会使用的空闲内存分配给其它程序使用,以提高内存利用率,这种技术叫做Overcommit。一般情况下,当所有程序都不会用到自己申请的所有内存时,系统不会出问题,但是如果程序随着运行,需要的内存越来越大,在自己申请的大小范围内,不断占用更多内存,直到超出物理内存,当linux发现内存不足时,会发生OOMkiller(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程,哪些占用内存越多,运行时间越短的进程越有可能被杀掉),以便释放内存。3、如果缓存数据丢失也可以接受,可以关闭appendonlyappendonlyon

  • 20

    08/05

    使用axios发送post请求,body传送数据格式form和json区别

    先来看看这两个种传送格式的写法1.form格式,将Content-Type类型设置为application/x-www-form-urlencode,POST请求时将data序列化,提交的数据会按照key1=val1&key2=val2的方式进行编码,key和val都进行了URL转码varinstance=axios.create({baseURL:conf.api.api_owt,timeout:60000,headers:{"Content-Type":"application/x-www-form-urlencoded;charset=utf-8;"}});//httprequest拦截器instance.interceptors.request.use(config=>{//POST传参序列化if(config.method==="post"){config.data=qs.stringify(config.data);}returnconfig;},error=>{returnPromise.reject(error);});2.json格式,有时候后台需要body传送的是json数据,将Content-Type类型设置为application/json,注意POST请求时data不要序列化varinstance=axios.create({baseURL:conf.api.api_owt,timeout:60000,headers:{"Content-Type":"application/json;"}});

  • 20

    08/05

    JS切割截取字符串方法总结

    1.函数:split()功能:使用一个指定的分隔符把一个字符串分割存储到数组例子:str=”jpg|bmp|gif|ico|png”;arr=str.split(”|”);//arr是一个包含字符值”jpg”、”bmp”、”gif”、”ico”和”png”的数组2.函数:join()功能:使用您选择的分隔符将一个数组合并为一个字符串例子:varmyList=newArray(”jpg”,”bmp”,”gif”,”ico”,”png”);varportableList=myList.join(”|”);//结果是jpg|bmp|gif|ico|png3.函数:concat()功能:将两个数组连接在一起;例子:arr1=[1,2,3,4]  arr2=[5,6,7,8]  alert(arr1.concat(arr2))//结果为[1,2,3,4,5,6,7,8]  4.函数:charAt()功能:返回指定位置的字符。字符串中第一个字符的下标是0。如果参数index不在0与string.length之间,该方法将返回一个空字符串。例子:varstr='a,g,i,d,o,v,w,d,k,p'alert(str.charAt(2))//结果为g5.函数:slice()功能:arrayObject.slice(start,end)  start:必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1指最后一个元素,-2指倒数第二个元素,以此类推。  end:可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从start到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。  返回一个新的数组,包含从start到end(不包括该元素)的arrayobject中的元素。例子:varstr='ahji3o3s4e6p8a0sdewqdasj'  alert(str.slice(2,5))//结果ji3  6.函数:substring()定义和用法substring方法用于提取字符串中介于两个指定下标之间的字符。语法stringObject.substring(start,stop)start必需。一个非负的整数,规定要提取的子串的第一个字符在stringObject中的位置。stop可选。一个非负的整数,比要提取的子串的最后一个字符在stringObject中的位置多1。如果省略该参数,那么返回的子串会一直到字符串的结尾。返回一个新的字符串,该字符串值包含stringObject的一个子字符串,其内容是从start处到stop-1处的所有字符,其长度为stop减start。说明substring方法返回的子串包括start处的字符,但不包括end处的字符。如果start与end相等,那么该方法返回的就是一个空串(即长度为0的字符串)。如果start比end大,那么该方法在提取子串之前会先交换这两个参数。如果start或end为负数,那么它将被替换为0。例子:varstr='ahji3o3s4e6p8a0sdewqdasj'alert(str.substring(2,6))//结果为ji3o37.函数:substr定义和用法substr方法用于返回一个从指定位置开始的指定长度的子字符串。语法stringObject.substr(start[,length])参数start必需。所需的子字符串的起始位置。字符串中的第一个字符的索引为0。  length可选。在返回的子字符串中应包括的字符个数。说明如果length为0或负数,将返回一个空字符串。如果没有指定该参数,则子字符串将延续到stringObject的最后。举例:varstr="0123456789";  alert(str.substring(0));------------"0123456789"  alert(str.substring(5));------------"56789"  alert(str.substring(10));-----------""  alert(str.substring(12));-----------""  alert(str.substring(-5));-----------"0123456789"  alert(str.substring(-10));----------"0123456789"  alert(str.substring(-12));----------"0123456789"  alert(str.substring(0,5));----------"01234"  alert(str.substring(0,10));---------"0123456789"  alert(str.substring(0,12));---------"0123456789"  alert(str.substring(2,0));----------"01"  alert(str.substring(2,2));----------""  alert(str.substring(2,5));----------"234"  alert(str.substring(2,12));---------"23456789"  alert(str.substring(2,-2));---------"01"  alert(str.substring(-1,5));---------"01234"  alert(str.substring(-1,-5));--------""

  • 20

    08/05

    冒泡排序算法

    本系列排序包括十大经典排序算法。使用的语言为:Java结构为:定义抽象类Sort里面实现了,交换,大小比较等方法。例如交换两个值,直接传入下标就可以了。其他的具体排序的类都继承抽象类Sort。这样我们就能专注于算法本身。/**返回值等于0,代表array[i1]==array[i2]*返回值小于0,代表array[i1]<array[i2]*返回值大于0,代表array[i1]>array[i2]*/protectedintcmp(inti1,inti2){returnarray[i1].compareTo(array[i2]);}protectedintcmp(Tv1,Tv2){returnv1.compareTo(v2);}protectedvoidswap(inti1,inti2){Ttmp=array[i1];array[i1]=array[i2];array[i2]=tmp;}复制代码什么是冒泡排序冒泡排序(BubbleSort)是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。算法原理比较相邻的元素。如果第一个比第二个大,就交换他们两个。对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。算法分析时间复杂度若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:C=n-1,M=0。所以,冒泡排序最好的时间复杂度为O(n)。若初始文件是反序的,需要进行n-1趟排序。每趟排序要进行n-i次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:C=n(n-1)/2=O(n^2).M=3n(n-1)/2=O(n^2).冒泡排序的最坏时间复杂度为O(n^2)。综上,因此冒泡排序总的平均时间复杂度为O(n^2)。算法稳定性冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。是否是原地算法何为原地算法?不依赖额外的资源或者依赖少数的额外资源,仅依靠输出来覆盖输入空间复杂度为𝑂(1)的都可以认为是原地算法非原地算法,称为Not-in-place或者Out-of-place冒泡排序属于In-place代码代码一publicclassBubbleSort<TextendsComparable<T>>extendsSort<T>{@Overrideprotectedvoidsort(){for(intend=array.length-1;end>0;end--){for(intbegin=1;begin<=end;begin++){if(cmp(begin,begin-1)<0){//ayyay[begin]小于ayyay[begin-1]就交换swap(begin,begin-1);}}}}}复制代码优化我们知道,每次都是两两比较,如果已经拍好顺序了。可以提前终止排序publicclassBubbleSort1<TextendsComparable<T>>extendsSort<T>{@Overrideprotectedvoidsort(){//TODOAuto-generatedmethodstubfor(intend=array.length-1;end>0;end--){booleanisSorted=true;//定义布尔值isSorted来标记是否有交换for(intbegin=1;begin<=end;begin++){//ayyay[begin]小于ayyay[begin-1]就交换if(cmp(begin,begin-1)<0){swap(begin,begin-1);isSorted=false;}}if(isSorted){//来到这里,说明没有交换过。已经是完全有序的了。提前终止排序break;}}}}复制代码再次优化如果序列尾部已经局部有序,可以记录最后1次交换的位置,减少比较次数publicclassBubbleSort2<TextendsComparable<T>>extendsSort<T>{@Overrideprotectedvoidsort(){//TODOAuto-generatedmethodstubfor(intend=array.length-1;end>0;end--){intsortedIndex=1;for(intbegin=1;begin<=end;begin++){if(cmp(begin,begin-1)<0){swap(begin,begin-1);sortedIndex=begin;}}end=sortedIndex;}}}复制代码验证使用数据源如下Integer[]array={7,3,5,8,6,7,4,5,19,30,40,50};结果为:【BubbleSort】稳定性:true耗时:0.0s(0ms)比较次数:66交换次数:14【BubbleSort1】稳定性:true耗时:0.001s(1ms)比较次数:51交换次数:14【BubbleSort2】稳定性:true耗时:0.0s(0ms)比较次数:30交换次数:14可以明显感觉到做了优化之后,比较测试减少了。

  • 20

    08/05

    5 个 JS 数组技巧可提高你的开发技能

    在前端开发中,数组是经常会被用到的数组结构,今天,介绍5个处理数组技巧,希望能带给你们一些启发和帮助。废话不多说,让我们开始吧。1.随机排列在开发者,有时候我们需要对数组的顺序进行重新的洗牌。在JS中并没有提供数组随机排序的方法,这里提供一个随机排序的方法:functionshuffle(arr){vari,j,temp;for(i=arr.length-1;i>0;i--){j=Math.floor(Math.random()*(i+1));temp=arr[i];arr[i]=arr[j];arr[j]=temp;}returnarr;}2.唯一值在开发者,我们经常需要过滤重复的值,这里提供几种方式来过滤数组的重复值。使用Set对象使用Set()函数,此函数可与单个值数组一起使用。对于数组中嵌套的对象值而言,不是一个好的选择。constnumArray=[1,2,3,4,2,3,4,5,1,1,2,3,3,4,5,6,7,8,2,4,6];//使用Array.from方法Array.from(newSet(numArray));//使用展开方式[...newSet(numArray)]使用Array.filter使用filter方法,我们可以对元素是对象的进行过滤。constdata=[{id:1,name:'Lemon'},{id:2,name:'Mint'},{id:3,name:'Mango'},{id:4,name:'Apple'},{id:5,name:'Lemon'},{id:6,name:'Mint'},{id:7,name:'Mango'},{id:8,name:'Apple'},]functionfindUnique(data){returndata.filter((value,index,array)=>{if(array.findIndex(item=>item.name===value.name)===index){returnvalue;}})}3.使用loadsh的lodash方法import{uniqBy}from'lodash'constdata=[{id:1,name:'Lemon'},{id:2,name:'Mint'},{id:3,name:'Mango'},{id:4,name:'Apple'},{id:5,name:'Lemon'},{id:6,name:'Mint'},{id:7,name:'Mango'},{id:8,name:'Apple'},]functionfindUnique(data){returnuniqBy(data,e=>{returne.name})}3.按属性对对象数组进行排序我们知道JS数组中的sort方法是按字典顺序进行排序的,所以对于字符串类,该方法是可以很好的正常工作,但对于数据元素是对象类型,就不太好使了,这里我们需要自定义一个排序方法。在比较函数中,我们将根据以下条件返回值:小于0:A在B之前大于0:B在A之前等于0:A和B彼此保持不变constdata=[{id:1,name:'Lemon',type:'fruit'},{id:2,name:'Mint',type:'vegetable'},{id:3,name:'Mango',type:'grain'},{id:4,name:'Apple',type:'fruit'},{id:5,name:'Lemon',type:'vegetable'},{id:6,name:'Mint',type:'fruit'},{id:7,name:'Mango',type:'fruit'},{id:8,name:'Apple',type:'grain'},]functioncompare(a,b){//UsetoLowerCase()toignorecharactercasingconsttypeA=a.type.toLowerCase();consttypeB=b.type.toLowerCase();letcomparison=0;if(typeA>typeB){comparison=1;}elseif(typeA<typeB){comparison=-1;}returncomparison;}data.sort(compare)4.把数组转成以指定符号分隔的字符串JS中有个方法可以做到这一点,就是使用数组中的.join()方法,我们可以传入指定的符号来做数组进行分隔。constdata=['Mango','Apple','Banana','Peach']data.join(',');//return"Mango,Apple,Banana,Peach"5.从数组中选择一个元素对于此任务,我们有多种方式,一种是使用forEach组合if-else的方式,另一种可以使用filter方法,但是使用forEach和filter的缺点是:在forEach中,我们要额外的遍历其它不需要元素,并且还要使用if语句来提取所需的值。在filter方法中,我们有一个简单的比较操作,但是它将返回的是一个数组,而是我们想要是根据给定条件从数组中获得单个对象。为了解决这个问题,我们可以使用find函数从数组中找到确切的元素并返回该对象,这里我们不需要使用if-else语句来检查元素是否满足条件。constdata=[{id:1,name:'Lemon'},{id:2,name:'Mint'},{id:3,name:'Mango'},{id:4,name:'Apple'}]constvalue=data.find(item=>item.name==='Apple')//value={id:4,name:'Apple'}作者:GhaziKhan译者:前端小智来源:codewithghazi

  • qq

    联系QQ:

    1149675376

  • wechat

    客服微信:

    406319040

  • phone

    咨询热线:

    0356-2618837

  • address

    公司地址:

    如何才能让自己戒赌市城区凤台西街太行日报纸库综合楼四楼