热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaScript类型系统之正则表达式

正则又叫规则或模式,是一个强大的字符串匹配工具。javascript通过RegExp类型来支持正则表达式,本文给大家介绍javascript类型系统之正则表达式,对js正则表达式相关知识感兴趣的朋友一起学习吧

定义

  正则又叫规则或模式,是一个强大的字符串匹配工具。Javascript通过RegExp类型来支持正则表达式

特性

  [1]贪婪性,匹配最长的
  [2]懒惰性,不设置/g,则只匹配第1个

写法

perl写法(使用字面量形式)
var expression = /pattern/flags;

  模式(pattern)部分可以是任何简单或复杂的正则表达式,可以包含字符串、限定类、分组、向前查找以及反向引用。每个正则表达式可以带一个或多个标志(flags),用以标明正则表达式的行为。正则表达式支持三个标志:

  [1]g:表示全局模式(global)
  [2]i:表示不区分大小写(ignoreCase)
  [3]m:表示多行模式(multiline)

//匹配字符串所有'at'的实例
var pattern1 = /at/g; 

RegExp构造函数

  RegExp构造函数接收两个参数:要匹配的字符串模式(pattern)和标志字符串(flags)(可选)

  [注意]RegExp构造函数的两个参数都是字符串。且使用字面量形式定义的任何表达式都可使用构造函数

//匹配字符串所有'at'的实例
var pattern = new RegExp('at','g'); 

两种写法的区别

  字面量写法不支持变量,只能用构造函数的形式来写
[tips]获取class元素(因为classname是变量,只能使用构造函数的形式)

function getByClass(obj,classname){
  var elements = obj.getElementsByTagName('*');
  var result = [];
  var pattern = new RegExp( '(^|\\s)'+ classname + '(\\s|$)');
  for(var i = 0; i 

  [注意]ES3中,正则表达式字面量始终共享同一个RegExp实例,而使用构造函数创建的每一个新RegExp实例都是一个新实例。ES5中规定使用正则字面量必须像直接调用RegExp构造函数一样,每次都创建新的RegExp实例

语法

  [注意]正则表达式中不能出现多余空格

元字符(14个)

    () [] {} \ ^ $ | ? * + . 
  [注意]元字符必须转义,即用\ 加转义字符,用new RegExp写的正则必须双重转义

转义字符

.      除换行符\n之外的任意字符
\d     数字
\D     非数字
\w     字母、数字、下划线
\W     非字母、数字、下划线(汉字不属于\w)
\s     空格
\S     非空格
\b     边界符(\w的左侧或右侧不是\w,则会出现一个边界符)
\B     非边界符
\1     表示和前面相同的一个字符
\t     制表符
\v     垂直制表符
\uxxxx 查找以十六进制xxxx规定的Unicode字符(\u4e00-\u9fa5代表中文)    
(\w)(\d)\1\2 :\1代表\w当时所代表的值,\2代表\d当时所代表的值

  [注意]正则表达式中的子项必须是用小括号括起来的,并且顺序以小括号的前括号出现的顺序为准

[tips]找出重复项最多的字符和个数

var str = 'aaaaabbbbbdddddaaaaaaaffffffffffffffffffgggggcccccce';
var pattern = /(\w)\1+/g;
var maxLength = 0;
var maxValue = '';
var result = str.replace(pattern,function(match,match1,pos,originalText){
  if(match.length > maxLength){
    maxLength = match.length;
    maxValue = match1;
  }
})
console.log(maxLength,maxValue);//18 "f" 

系统转义

  alert()和console.log()里面的字符是系统转义字符

\0 空字节
\n 换行
\t 制表
\b 空格
\r 回车
\f 进纸
\\ 斜杠
\' 单引号
\" 双引号
\xnn 以十六进制nn表示一个字符(n为0-f),如\x41表示'A'
\unnnn 以十六进制nnnn表示一个Unicode字符(n为0-f),如\u03a3表示希腊字符ε

  [注意]alert里面的换行不能用
,而应该用\n

 alert('http://www.baidu.com\n\t你好') 

双重转义

  由于RegExp构造函数的参数是字符串,所以某些情况下,需要对字符进行双重转义。所有元字符必须双重转义,已经转义过的字符也必须双重转义

字面量模式      ->     等价的字符串
/\[bc\]at/           "\\[bc\\]at"
/\.at/             "\\.at"
/name\/age/           "name\\/age"
/\d.\d{1,2}/          "\\d.\\d{1,2}"
/\w\\hello\\123/        "\\w\\\\hello\\\\123" 

量词

{n}       匹配n次
{n,m}     匹配至少n次,最多m次
{n,}      匹配至少n次
?         相当于{0,1}
*         相当于{0,}
+         相当于{1,}

位置符号

^        起始符号
$        结束符号
?=       肯定正向环视
?!       否定正向环视

控制符号

[]     候选
|      或 
^      非
-      到

(red|blue|green)       查找任何指定的选项       
[abc]                  查找方括号之间的任何字符
[^abc]                 查找任何不在方括号之间的字符
[0-9]                  查找任何从0到9的数字
[a-z]                  查找任何从小写a到小写z的字符
[A-Z]                  查找任何从大写A到大写Z的字符
[A-z]                  查找任何从大写A到小写z的字符
[adgk]                 查找给定集合内的任何字符
[^adgk]                查找给定集合外的任何字符

$符号

$$         $
$&         匹配整个模式的子字符串(与RegExp.lastMatch的值相同)
$`         匹配子字符串之前的子字符串(与RegExp.leftContext的值相同)
$'         匹配子字符串之后的子字符串(与RegExp.rightContext的值相同)
$n         匹配第n个捕获组的子字符串,其中n等于0-9。$1表示匹配第一个捕获组的子字符串(从第1个算起)
$nn        匹配第nn个捕获组的子字符串,其中nn等于01-99

console.log('cat,bat,sat,fat'.replace(/(.a)(t)/g,'$0'))//$0,$0,$0,$0      
console.log('cat,bat,sat,fat'.replace(/(.a)(t)/g,'$1'))//ca,ba,sa,fa
console.log('cat,bat,sat,fat'.replace(/(.a)(t)/g,'$2'))//t,t,t,t
console.log('cat,bat,sat,fat'.replace(/(.a)(t)/g,'$3'))//$3,$3,$3,$3      
console.log('cat,bat,sat,fat'.replace(/(.a)(t)/g,'$$'))//$,$,$,$
console.log('cat,bat,sat,fat'.replace(/(.a)(t)/g,'$&'))//cat,bat,sat,fat
console.log('cat,bat,sat,fat'.replace(/(.a)(t)/g,'$`'))//,cat,,cat,bat,,cat,bat,sat,
console.log('cat,bat,sat,fat'.replace(/(.a)(t)/g,"$'"))//,bat,sat,fat,,sat,fat,,fat, 

构造函数属性

  适用于作用域中的所有正则表达式,并且基于所执行的最近一次正则表达式操作而变化。关于这些属性的独特之处在于可以通过两种方式访问它们,即长属性名和短属性名。短属性名大都不是有效的ECMAScript标识符,所以必须通过方括号语法来访问它们

长属性名        短属性名                说明
input             $_                最近一次要匹配的字符串
lastMatch         $&                最近一次的匹配项
lastParen         $+                最近一次匹配的捕获组
leftContext       $`                input字符串中lastMatch之前的文本
multiline         $*                布尔值,表示是否所有表达式都使用多行模式
rightContext      $'                Input字符串中lastMarch之后的文本

  使用这些属性,可以从exec()方法或text()方法执行的操作中提取出更具体的信息

var text = 'this has been a short summer';
var pattern = /(.)hort/g;
if(pattern.test(text)){
  console.log(RegExp.input);//'this has been a short summer'
  console.log(RegExp.leftContext);//'this has been a '
  console.log(RegExp.rightContext);//' summer'
  console.log(RegExp.lastMatch);//'short'
  console.log(RegExp.lastParen);//'s'
  console.log(RegExp.multiline);//false
  console.log(RegExp['$_']);//'this has been a short summer'
  console.log(RegExp['$`']);//'this has been a '
  console.log(RegExp["$'"]);//' summer'
  console.log(RegExp['$&']);//'short'
  console.log(RegExp['$+']);//'s'
  console.log(RegExp['$*']);//false    
} 

//Javascript有9个用于存储捕获组的构造函数属性。RegExp.$1\RegExp.$2\RegExp.$3……到RegExp.$9分别用于存储第一、第二……第九个匹配的捕获组。在调用exec()或test()方法时,这些属性会被自动填充

var text = 'this has been a short summer';
var pattern = /(..)or(.)/g;
if(pattern.test(text)){
  console.log(RegExp.$1);//sh
  console.log(RegExp.$2);//t
}

 实例属性

  通过实例属性可以获知一个正则表达式的各方面信息,但却没多大用处,因为这些信息都包含在模式声明中
global:    布尔值,表示是否设置了g标志
ignoreCase: 布尔值,表示是否设置了i标志
lastIndex:  整数,表示开始搜索下一个匹配项的字符位置,从0算起
multiline:  布尔值,表示是否设置了标志m
source:    正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回

var pattern = new RegExp('\\[bc\\]at','i');
console.log(pattern.global);//false
console.log(pattern.ignoreCase);//true  
console.log(pattern.multiline);//false
console.log(pattern.lastIndex);//0
console.log(pattern.source);//'\[bc\]at' 

继承的方法

  共toString()、toLocaleString()和valueOf()三个方法,且都返回正则表达式字面量,与创建正则表达式的方式无关。要注意的是toString()和toLocaleString()返回的正则表达式的字符串表示,而valueOf返回的是正则表达式对象本身

var pattern = new RegExp('\\[bc\\]at','gi');
console.log(pattern.toString()); // '/\[bc\]at/gi'
console.log(pattern.toLocaleString()); // '/\[bc\]at/gi'
console.log(pattern.valueOf()); // /\[bc\]at/gi 

实例方法

exec()

  专门为捕获组而设计,接受一个参数,即要应用模式的字符串。然后返回包含第一个匹配项信息的数组。在没有匹配项的情况下返回null。返回的数组包含两个额外的属性:index和input。index表示匹配项在字符串的位置,input表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串,如果模式中没有捕获组,则该数组只包含一项

var text = 'mom and dad and baby and others';
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
console.log(pattern,matches);
//pattern.lastIndex:20
//matches[0]:'mom and dad and baby'
//matches[1]:' and dad and baby'
//matches[2]:' and baby'
//matches.index:0
//matches.input:'mom and dad and baby and others'  

  [注意]对于exec()方法而言,即使在模式中设置了全局标志(g),它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用exec(),将始终返回第一个匹配项的信息;而在设置全局标志的情况下,每次调用exec()都会在字符串中继续查找新匹配项。IE8-在lastIndex属性上存在偏差,即使在非全局模式下,lastIndex属性每次也会变化

var text = 'cat,bat,sat,fat';
var pattern1 = /.at/;
var matches = pattern1.exec(text);
console.log(pattern1,matches);
//pattern1.lastIndex:0
//matches[0]:'cat'
//matches.index:0
//matches.input:'cat,bat,sat,fat'
var text = 'cat,bat,sat,fat';
matches = pattern1.exec(text);  
console.log(pattern1,matches);  
//pattern1.lastIndex:0
//matches[0]:'cat'
//matches.index:0
//matches.input:'cat,bat,sat,fat' 
var text = 'cat,bat,sat,fat';
var pattern2 = /.at/g;
var matches = pattern2.exec(text);
console.log(pattern2,matches);  
//pattern2.lastIndex:3
//matches[0]:'cat'
//matches.index:0
//matches.input:'cat,bat,sat,fat'
var text = 'cat,bat,sat,fat';
matches = pattern2.exec(text);
console.log(pattern2,matches);  
//pattern2.lastIndex:7
//matches[0]:'bat'
//matches.index:4
//matches.input:'cat,bat,sat,fat'  

[tips]用exec()方法找出匹配的所有位置和所有值

var string = 'j1h342jg24g234j 3g24j1';
var pattern = /\d/g;
var valueArray = [];//值
var indexArray = [];//位置
var temp = pattern.exec(string);
while(temp != null){
  valueArray.push(temp[0]);
  indexArray.push(temp.index);
  temp = pattern.exec(string);  
}
//["1", "3", "4", "2", "2", "4", "2", "3", "4", "3", "2", "4", "1"] [1, 3, 4, 5, 8, 9, 11, 12, 13, 16, 18, 19, 21]
console.log(valueArray,indexArray);  

test()

  接受一个字符串参数,在模式与该参数匹配的情况下返回true,否则返回false
  [注意]常用于只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况,经常用在if语句中

var text = '000-00-000';
var pattern = /\d{3}-\d{2}-\d{4}/;
if(pattern.test(text)){
  console.log('The pattern was matched');
}

模式匹配方法

  String类型定义了几个用于在字符串中匹配模式的方法

match()

  只接受一个参数,正则或字符串,把匹配的内容保存到一个数组中返回
  [注意]加上全局标记时,match()方法返回值中没有index和input属性

[1]不加/g

var string = 'cat,bat,sat,fat';
var pattern = /.at/;
var matches = string.match(pattern);
console.log(matches,matches.index,matches.input);//['cat'] 0 'cat,bat,sat,fat' 

[2]加/g

var string = 'cat,bat,sat,fat';
var pattern = /.at/g;
var matches = string.match(pattern);
console.log(matches,matches.index,matches.input);//['cat','bat','sat','fat'] undefined undefined 

[3]字符串

var string = 'cat,bat,sat,fat';
var pattern = 'at';
var matches = string.match(pattern);
console.log(matches,matches.index,matches.input);//['at'] 1 'cat,bat,sat,fat' 
search()

  只接受一个参数,正则或字符串,返回匹配的内容在字符串中首次出现的位置,类似于不能设置起始位置的indexOf,找不到返回-1

[1]正则(加/g和不加/g效果一样)

var string = 'cat,bat,sat,fat';
var pattern = /.at/;
var pos = string.search(pattern);
console.log(pos);//0 

[2]字符串

var string = 'cat,bat,sat,fat';
var pattern = 'at';
var pos = string.search(pattern);
console.log(pos);//1 

[tips]找出匹配的所有位置

function fnAllSearch(str,pattern){
  var pos = str.search(pattern); 
  var length = str.match(pattern)[0].length;
  var index = pos+length;
  var result = [];
  var last = index;
  result.push(pos);
  while(true){
    str = str.substr(index);          
    pos = str.search(pattern);
    if(pos === -1){
      break;
    }
    length = str.match(pattern)[0].length;
    index = pos+length;
    result.push(last+pos);
    last += index;  
  }
  return result;
}  
console.log(fnAllSearch('cat23fbat246565sa3dftf44at',/\d+/));//[3,9,17,22] 

replace()

  该方法接收两个参数:第一个为正则表达式或字符串(待查找的内容)、第二个为字符串或函数(替换的内容)

[1]字符串替换

var string = 'cat,bat,sat,fat';
var result = string.replace('at','ond');
console.log(result);//'cond,bat,sat,fat' 

[2]正则无/g替换

var string = 'cat,bat,sat,fat';
var result = string.replace(/at/,'ond');
console.log(result);//'cond,bat,sat,fat' 

[3]正则有/g替换

var string = 'cat,bat,sat,fat';
var result = string.replace(/at/g,'ond');
console.log(result);//'cond,bond,sond,fond' 

[4]函数替换

  在只有一个匹配项(即与模式匹配的字符串的情况下,会向这个函数传递3个参数:模式的匹配项、模式匹配项在字符串中的位置、原始字符串。在正则表达式定义了多个捕获组的情况下,传递给函数的参数依次是模式的匹配项、第一个捕获组的匹配项、第二个捕获组的匹配项……第N个捕获组的匹配项,但最后两个参数仍然分别是模式的匹配项在字符串中的位置和原始字符串,这个函数返回一个字符串

var string = 'cat,bat,sat,fat';
var index = 0;
var result = string.replace(/at/g,function(match,pos,originalText){
  index++;
  if( index== 2){
    return 'wow';
  }else{
    return '0';
  }
});
console.log(result);//'c0,bwow,s0,f0' 

[tips]防止跨站脚本攻击xss(css)

function htmlEscape(text){
  return text.replace(/[<>"&]/g,function(match,pos,originalText){
    switch(match){
      case '<':
      return '<';
      case '>':
      return '>';
      case '&':
      return '&';
      case '\"':
      return '"';
    }
  });
}
console.log(htmlEscape('

Hello world!

')); //

Hello world!

console.log(htmlEscape('

Hello world!

')); //同上

split()

  这个方法可以基于指定的分隔符将一个字符串分割成多个字符串,并将结果放在一个数组中,分隔符可以是字符串,也可以是一个RegExp。该方法可以接受第二个参数(可选)用于指定数组的大小,如果第二个参数为0-array.length范围内的值时按照指定参数输出,其他情况将所有结果都输出

  [注意]IE8-对split()中的正则表达式,会忽略捕获组

[tips]如果是split(''),则原来的数组会一个字符字符分割后传出来

var colorText = 'red,blue,green,yellow';
console.log(colorText.split(''));//["r", "e", "d", ",", "b", "l", "u", "e", ",", "g", "r", "e", "e", "n", ",", "y", "e", "l", "l", "o", "w"]
console.log(colorText.split(','));//["red", "blue", "green", "yellow"]
console.log(colorText.split(',',2));//["red", "blue"]
console.log(colorText.split(/\,/));//["red", "blue", "green", "yellow"]
console.log(colorText.split(/e/));//["r", "d,blu", ",gr", "", "n,y", "llow"]
console.log(colorText.split(/[^\,]+/));//将除去逗号以外的字符串变为分隔符["", ",", ",", ",", ""],IE8-会识别为[",",",",","] 

 局限性

  下列为ECMAScript正则表达式不支持的特性

  [1]匹配字符串开始的结尾的\A和\Z锚(但支持以^和$来匹配字符串的开始和结尾)
  [2]向后查找(但支持向前查找)
  [3]并集和交集类
  [4]原子组
  [5]Unicode支持(单个字符除外)
  [6]命名的捕获组(但支持编号的捕获组)
  [7]s(single单行)和x(free-spacing无间隔)匹配模式  
  [8]条件匹配
  [9]正则表达式注释

常见实例

[1]两种方法找出字符串中所有的数字
  [a]用传统字符串操作

var str1 = 'j1h342jg24g234j 3g24j1';
var array = [];
var temp = '';
for(var i = 0; i 

  [b]用正则表达式

var str1 = 'j1h342jg24g234j 3g24j1';
array = str1.match(/\d+/g);
console.log(array);//["1", "342", "24", "234", "3", "24", "1"] 

[2]敏感词过滤(replace方法的函数匹配)

var string = 'FLG是邪教';
var pattern = /FLG|邪教/g;
var result = string.replace(pattern,function($0){
  var s = '';
  for(var i = 0; i <$0.length; i++){
    s+= '*';
  }
  return s;
})
console.log(result);//***是** 

[3]日期格式化

var array = ['2015.7.28','2015-7-28','2015/7/28','2015.7-28','2015-7.28','2015/7---28'];
function formatDate(date){
  return date.replace(/(\d+)\D+(\d+)\D+(\d+)/,'$1'+'年'+'$2'+'月'+'$3'+'日')
}
var result = [];
for(var i = 0 ; i 

[4]获取网页中的文本内容

var str = '

refds

fasdf

' var pattern = /<[^<>]+>/g; console.log(str.replace(pattern,''));//refdsfasdf

[5]去除首尾空格的trim()兼容写法

var string = '  my name is littlematch  ';
console.log(string.replace(/^\s+|\s+$/,''));//my name is littlematch 

关于Javascript类型系统之正则表达式 的全部内容就给大家介绍到这里,希望本文所述能够帮助到大家。


推荐阅读
  • php课程Json格式规范需要注意的小细节
    JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgramming ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • jvm内存区域与溢出为什么学习jvm木板原理,最短的一块板决定一个水的深度,当一个系统垃圾收集成为瓶颈的时候,那么就需要你对jvm的了解掌握。当一个系统出现内存溢出,内存泄露的时候 ... [详细]
  • 前言对于从事技术的人员来说ajax是这好东西,都会使用,而且乐于使用。但对于新手,开发一个ajax实例,还有是难度的,必竟对于他们这是新东西。leo开发一个简单的ajax实例,用的是 ... [详细]
  • PHP编程能开发哪些应用?
    导读:很多朋友问到关于PHP编程能开发哪些应用的相关问题,本文编程笔记就来为大家做个详细解答,供大家参考,希望对大家有所帮助!一起来看看吧!本文目录一览: ... [详细]
  • JVM(三)虚拟机栈 多合一总述
    虚拟机栈概述虚拟机栈出现背景:由于跨平台性的设计,Java的指令都是根据栈来设计的。不同CPU架构不同,所以不能设计为基于寄存器的跨平台的优点:指令集小,编译器容易实现,缺点是性能 ... [详细]
  • php生成shtml类用法的简单介绍
    本文目录一览:1、phpcmsv9怎么生成shtml ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 让你明白kvm是什么
    参考:https:blog.csdn.netbbwangjarticledetails80465320KVM工具集合:libvirt:操作和管理KVM虚机的虚拟化API ... [详细]
author-avatar
真知棒
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有