严格模式/解构赋值/简单正则

本文详细介绍了JavaScript的严格模式,包括启用方式、与松散模式的区别,如形参与arguments的关系、this的指向变化等。接着讨论了错误类型及throw关键字的用法。然后深入解构赋值,涵盖数组、对象、字符串和函数参数的解构,并讲解了默认值和重命名。最后,文章探讨了正则表达式的基本概念、创建方式、元字符、修饰符和量词的应用。
一.严格模式
  1. 严格模式的意义:
    • 一把情况下,js是运行在松散模式下.为了让语言越来越规范,引入了严格模式.
  2. 使用严格模式
    • 在脚本的第一行增加一个字符串"use strict",表示当前脚本使用严格模式
  3. 松散模式与严格模式的常见区别
    • 1.在松散模式下,形参跟arguments是有映射关系的,但是在严格模式下形参和arguments没有映射关系,如果在函数中修改形参的值,arguments里的值也会随之修改.
    function sum(a, b) {
    b = 100; // 普通模式中
    console.log(arguments); // 1 100
    }
    sum(1, 3);
    
    
    'use strict';
    function sum2(a, b) {
    b = 100;   //严格模式中
    console.log(arguments); // 1 2
    }
    sum2(1, 2);
    
    注意点:使用webpack打包时,会吧js文件默认添加 "use strict"
    • 2.在严格模式下,通过call方法改变this指向时与松散模式有一些不同.
      • 不传参时,this指向undefined
      • 传参时,this指定谁,谁就是this
    'use strict';
    function f1() {
    console.log(this);
    }
    f.call(); // 输出 undefined
    f1.call(undefined); //输出 undefined
    f1.call(null); // 输出 null
    //非严格模式下第一次输出 window
    //非严格模式下第二次输出 window
    //非严格模式下第三次输出 window
    
    • 3.严格模式下,我们不能直接给未声明的变量赋值,如果赋值会报错
    'use strict';
     aa= 123; // 非严格模式下给window增加属性
     aa = 123; // 严格模式下会报错: Uncaught Reference:aa is not defined
    
二. 错误类型
  1. throw关键字
    • throw的定义
      • 用于手动抛出异常(错误),抛出异常后,后面的代码就不会执行了(同步情况下不执行).
      • throw关键字后面无论写什么都会当成错误抛出
       throw 'hello world';
       console.log(123); // 不会输出,因为throw关键字抛出的是错误,js一旦抛出错误后就会停止后面的代码执行;
    
  2. 常见的内置错误类型
    1. ReferencrError 引用错误
      (一般由于引用未声明的变量导致)
    2. TypeError 类型错误
      (一般由于搞错类型导致 比如:执行一个基本数据类型的变量)
    3. SyntaxError 语法错误
      (一般因为语法写的不对)
    4. Error 普通错误
三. 解构赋值
  1. 定义:利用数据结构的对应关系,快速的对对象和数组进行取值(Es6新增)
  2. 解构赋值的分类
    1. 数组的解构赋值
      • 1.顺序解构:变量放在等号左边,数组放在等号右边,左边变量取出来的值是和右边数组的索引一一对应的.
          let ary = [10, 50, 100, 200];
          let [a, b, c] = ary; 
          console.log(a, b, c);//10,50,100
      
      • 2 只取数组中的某一个
      let [,, d] = ary; //如果只想得到第三个,前面两个空着,但是需要写两个 ,
      console.log(d);//200
      
      • 3 进一步解构:取数组中的数组
      let ary2 = [10, ['12', '13']];
      let [g, [h, i]] = ary2;
      console.log(g,h,i); // 10,12,13
      
      • 4 取出数组中的第一项跟第二项,并把剩下的放到一个数组中
      let ary = [10, 50, 100, 200]
      let [e, f, ...arr] = ary;
      console.log(e,f,arr); // 10 50 [100,200]
      
      • 5 数组解构时可以添加默认值
        • 1 当变量项比数组项多时,多出来的变量默认为undefined,如果这时设置了变量项的默认值,那么会解构出默认值
        • 2 当同时存在可以解构的值和默认值时,会优先使用可以解构的值,但是当可以解构的值为undefinde时,会优先使用默认值
        let ary3 = [1, 3];
        
        let [j = 2, k = 3, l = 100] = ary3; // j的默认值是2,k的默认值是3,l的默认值是100
        console.log(j); // 2
        console.log(k); // 3
        console.log(l); // 100
        
        • 3 当解构变量互为默认值时
        let [b=a,a]=[undefined,1];
        console.log(b,a)// 报错:a is not defined, 1
        
    2. 对象的解构赋值
    • 1.解构赋值的意义:
      • 通常情况下,我们使用对象.属性名 或者对象[“属性名”]取得属性名对应的属性.
        而对象的解构赋值就是通过变量和对象的属性对应时,就可以取得对象的改属性名的值
    • 2.解构赋值
    let obj={name:"帅哥",age:18}
    let { name, age } = obj;
    console.log(name);//帅哥
    console.log(age);//18
    
    • 3.进一步解构:
    let obj={a:1,b:{c:2}};
    let {num{b:{c}}=obj
    console.log(num)//2;
    
    • 4.重命名:防止和其他变量名冲突;
    let obj={name:"dadapeng"}
    let {name} = obj
    let {name:name2}=obj
    
    • 5.对象解构的默认值
    let obj={name="帅哥"};
    let {name=name2,age="3"}=obj;
    console.log(name,age)//帅哥,3;
    
    • 6.字符串的解构赋值:跟对象的解构赋值一样,根据位置解构对应的值.
    let [x,y,z]="hello"
    console.log(x,y,z)//h,e,l
    
    • 7.解构函数的实参
    function fun({name,age}){
        console.log(name,age)//帅哥,18
    }
    fun({name:"帅哥",age:18});
    
四. 正则

正则:RegExp (regular expression的缩写)

  1. 正则是一个专门用来处理字符串的规则,这种处理分为两种:
    • 判断字符串是否符合这个规则,即正则匹配
    • 把符合规则的字符串获取到,即正则的捕获
  2. 创建一个正则,分为两种方式:
    • let reg = /^\d+$/g; 字面量创建
    • let reg2= new RegExp("\d+",“g”) //构造函数创建
  3. 正则的构成(字面量):正则是由元字符和修饰符组成,两个斜线中包裹的叫做元字符
    • 元字符:
      • 特殊元字符
        • \d 0-9之间的任意一个数字
        • \D 除了0-9之间的任意一个字符
        • \w 字母,数字,_(下划线)中的任意一个
        • \W 除了字母,数字,_(下划线)中的任意一个
        • \s 匹配一个空白符(包括空格,制表符(\t)也就是Tab,一个Tab代表4个空格)
        • \b 匹配边界,‘zhu-feng’ 边界字母和非字母链接的地方,z的左边,u的右边,f的左边,g的右边都是边界
        • \n 匹配一个换行符
        • . 通配符 匹配除了换行符\n以外的任意一个字符
        • \ 转译符 1. 把具有特殊意义的字符转义成普通字符.比如:正则中的.表示匹配除了换行符\n以外的任意一个字符,而转义符+.则只表示一个普通的小数点
          2.还可以把普通元字符转义成特殊元字符,例如转义符+d表示0-9的数字.
        • ^ 表示以某个元字符开头
        • $ 表示以某个元字符结尾
        • | 表示或者 比如: x|y 表示x或者y中的任意一个
        • [xyz] 表示x,y,z中的任意一个数字
        • [a-z] 表示a-z中的任意一个小写字母
        • [0-9] 表示0-9中的任意一个数字
        • [A-Z] 表示A-Z中的任意一个大写字母
        • [^abc] 表示除了a,b,c以外的任意一个字母
        • ()正则分组
        • (?:)表示当前分组只匹配不捕获
        • (?=)正向预查
        • (?!)负向预查
      • 量词元字符:表示个数
        • *号 表示出现0-1次
        • ? 表现出现0次到多次
        • +号 表示出现1次到多次
        • {n} 表示出现n次
        • {n,}表示至少出现n次
        • {n,m}表示出现n到m次
      • 普通元字符:除特殊原字符和量词元字符以外的元字符
    • 修饰符:
      • i 忽略大小写
      • m 多行匹配
      • g 全局匹配
<think>我们要将字符串从"yyyy/MM/dd"格式转换为"yyyy-MM-dd"格式。为了健壮性,我们应该考虑以下几点:1.输入字符串的格式可能不是严格的"yyyy/MM/dd",可能有其他分隔符(比如用户输入的分隔符可能是其他字符,但要求是日期格式相同,即四位数年,两位数月,两位数日)。2.我们不应该依赖于固定的分隔符,而是应该提取出年、月、日三个部分,然后用新的分隔符连接。因此,我们可以使用正则表达式来匹配数字部分,然后重新组合。或者,我们可以使用日期解析库(如Java中的SimpleDateFormat或DateTimeFormatter)来解析字符串,然后再格式化成目标格式。但是,如果输入的分隔符不是预期的,解析库可能会抛出异常,所以我们需要考虑异常处理。另外,如果要求不依赖日期库,我们可以采用字符串分割的方法,但是分割时要注意,分隔符可能是任意非数字字符(但这里我们假设日期字符串由非数字的分隔符分开,且年、月、日的位置固定)。这里提供两种方法:方法一:使用字符串替换(不推荐,因为不够健壮,如果输入分隔符不是'/',而是其他字符,替换可能失效)例如:input.replace('/','-')但是如果输入是"yyyy.MM.dd",那么替换后还是点,不符合要求。方法二:提取数字部分,然后重新组合。我们可以用正则表达式匹配数字,然后忽略非数字部分。下面分别用不同语言实现方法二:例如在Java中,我们可以这样做:使用正则表达式匹配三个数字组,然后组合成目标字符串。但是,更健壮的方法是使用日期时间库来解析,然后重新格式化。这样还可以验证日期的有效性。但题目要求是字符串转换,且不失效,所以如果输入日期无效,我们可以选择抛出异常或按原样转换(但无效日期可能不应该被转换)。考虑到健壮性,我们使用日期库解析然后格式化,这样即使分隔符变化了,只要日期格式是年、月、日顺序,并且数字位数正确,就可以转换。同时,这种方法还能验证日期的有效性(比如2月30日会被认为是无效的,解析会失败)。但是,如果输入的分隔符是任意的,而且我们不想验证日期,只是想转换格式,那么我们可以用正则表达式提取数字。下面提供两种思路的代码:思路1:正则表达式提取数字(不验证日期)思路2:使用日期库(验证日期,更健壮)我们分别实现:在Java中,使用正则表达式提取:我们可以使用Pattern和Matcher,或者使用String.split,但是split需要知道分隔符,而分隔符可能是任意非数字字符,所以我们可以用非数字字符作为分隔符。例如:String[]parts=input.split("\\D+");注意:split可能会产生空字符串,比如开头有非数字字符,所以我们需要过滤掉空字符串,并且取前三个非空部分。但是,更简单的是用正则表达式匹配数字:代码示例(Java):```javaimportjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassDateConverter{publicstaticStringconvertDateString(Stringinput)throwsIllegalArgumentException{//正则表达式匹配连续的4位数字(年)和1-2位数字(月、日)Patternpattern=Pattern.compile("\\b(\\d{4})\\D+(\\d{1,2})\\D+(\\d{1,2})\\b");Matchermatcher=pattern.matcher(input);if(matcher.find()){Stringyear=matcher.group(1);Stringmonth=String.format("%02d",Integer.parseInt(matcher.group(2)));//确保两位Stringday=String.format("%02d",Integer.parseInt(matcher.group(3)));returnyear+"-"+month+"-"+day;}else{thrownewIllegalArgumentException("Invaliddateformat");}}}```但是,上述正则表达式可能不够健壮,因为日期字符串可能没有单词边界(\b),并且我们假设了顺序。另外,它不验证日期的有效性(比如月份是否在1-12,日期是否合理)。使用日期库(Java8之后的DateTimeFormatter):```javaimportjava.time.LocalDate;importjava.time.format.DateTimeFormatter;importjava.time.format.DateTimeParseException;publicclassDateConverter{publicstaticStringconvertDateString(Stringinput)throwsDateTimeParseException{//尝试用多种分隔符解析,但实际上DateTimeFormatter.ofPattern可以处理任意单个字符的分隔符吗?//实际上,模式字符串中的'/'会匹配任意分隔符吗?不会,它只匹配'/'。所以我们需要一个更灵活的方式。//我们可以用一个DateTimeFormatter来解析,它使用模式"[yyyy][MM][dd]"并不行,因为分隔符是固定的。//另一种思路:先标准化输入字符串,将非数字字符替换成统一的分隔符(比如'/'),然后用固定格式解析。//但是,如果输入的分隔符不一致,比如"2020/01-01",这就不太好处理。//因此,我们使用DateTimeFormatterBuilder,指定解析可以忽略分隔符?或者使用多个格式化器尝试?//更简单的方法:我们用一个正则表达式将输入字符串中的非数字字符替换成'/',然后再解析。但这样可能会把无效日期变成有效日期?比如"2020-01/02"->"2020/01/02"然后解析成2020年1月2日。//步骤://1.将输入字符串中的非数字字符(除了可能存在的空格)替换成斜杠,并去除空格。//2.用"yyyy/MM/dd"格式解析。Stringnormalized=input.replaceAll("\\s+","").replaceAll("[^0-9]+","/");//然后分割,并确保每个部分都是数字,且长度符合要求?或者直接解析DateTimeFormatterparser=DateTimeFormatter.ofPattern("yyyy/M/d");LocalDatedate=LocalDate.parse(normalized,parser);//然后格式化成目标格式DateTimeFormatterformatter=DateTimeFormatter.ofPattern("yyyy-MM-dd");returnformatter.format(date);}}```但是,上面的方法可能会将"2020-01-02"和"2020/01/02"都标准化成相同的字符串,但是也可能将"2020年01月02日"变成"2020/01/02",所以可以工作。但是,如果输入字符串中有多余的非数字字符,比如"Date:2020-01-02",那么标准化后变成"Date/2020/01/02",解析就会失败。所以,我们改进:只提取数字部分,并用斜杠连接。我们可以用正则表达式提取连续的数字:```javaimportjava.time.LocalDate;importjava.time.format.DateTimeFormatter;importjava.time.format.DateTimeParseException;importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassDateConverter{publicstaticStringconvertDateString(Stringinput)throwsIllegalArgumentException{Patternpattern=Pattern.compile("(\\d{4})[^0-9]+(\\d{1,2})[^0-9]+(\\d{1,2})");Matchermatcher=pattern.matcher(input);if(matcher.find()){Stringnormalized=matcher.group(1)+"/"+matcher.group(2)+"/"+matcher.group(3);try{DateTimeFormatterparser=DateTimeFormatter.ofPattern("yyyy/M/d");LocalDatedate=LocalDate.parse(normalized,parser);DateTimeFormatterformatter=DateTimeFormatter.ofPattern("yyyy-MM-dd");returnformatter.format(date);}catch(DateTimeParseExceptione){thrownewIllegalArgumentException("Invaliddate:"+normalized,e);}}else{thrownewIllegalArgumentException("Nodatefoundininput:"+input);}}}```这样,我们先提取数字部分,然后组成一个标准格式的字符串(用斜杠分隔),再用日期库解析,最后格式化为目标格式。这样既处理了任意分隔符,又验证了日期的有效性。在其他语言中,思路类似。例如在Python中:使用正则表达式提取,然后用datetime解析:```pythonimportrefromdatetimeimportdatetimedefconvert_date_string(input_str):#匹配连续的四位数字(年)和1-2位数字(月、日)pattern=r'(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})'match=re.search(pattern,input_str)ifmatch:year=match.group(1)month=match.group(2)day=match.group(3)normalized=f"{year}/{month}/{day}"try:#解析日期,格式为年/月/日,注意月份和日可能是1位或2位date_obj=datetime.strptime(normalized,"%Y/%m/%d")#格式化为目标字符串returndate_obj.strftime("%Y-%m-%d")exceptValueErrorase:raiseValueError(f"Invaliddate:{normalized}")fromeelse:raiseValueError(f"Nodatefoundininput:{input_str}")#测试print(convert_date_string("2020/01/01"))#2020-01-01print(convert_date_string("2020-01-01"))#2020-01-01print(convert_date_string("2020年01月01日"))#2020-01-01print(convert_date_string("Date:2020.01.01"))#2020-01-01```这样,无论输入的分隔符是什么(只要年月日顺序正确,且被非数字字符分隔),都可以转换。但是,如果输入字符串中有多组数字,比如"abc2020/01/01def",我们的正则表达式匹配第一组符合模式的部分。如果输入是"2020/01/01and2021/02/02",那么会匹配第一组。如果输入是"Version1.0.0releasedon2020-01-01",那么也会匹配到2020-01-01。如果输入中有多组日期,我们可能需要更复杂的规则,但题目没有要求,我们默认输入字符串中只包含一个日期。因此,这种方法比较健壮。总结:我们使用正则表达式提取年月日,然后用日期库解析并重新格式化,这样可以处理任意分隔符,并且验证日期的有效性。如果不希望验证日期(即即使日期无效,如2月30日,也进行转换),那么我们可以跳过日期解析,直接组合。但是这样可能产生非法日期字符串,所以一般不推荐。所以,我们推荐使用日期库验证的方法。现在,根据要求,我们给出代码并解释。注意:上面的代码已经给出了Java和Python的示例。下面再给出JavaScript的示例(在Node.js或浏览器环境中):```javascriptfunctionconvertDateString(input){//正则表达式匹配:4位数字,然后非数字,然后1-2位数字,然后非数字,然后1-2位数字constpattern=/(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})/;constmatch=input.match(pattern);if(match){constyear=match[1];constmonth=match[2].padStart(2,'0');constday=match[3].padStart(2,'0');//不验证日期,直接组合//如果要求验证日期,可以使用Date对象,但要注意JavaScript的Date对象月份是从0开始的//验证日期constdate=newDate(`${year}-${month}-${day}`);//检查日期是否有效,并且年月日与输入匹配(因为Date会自动调整非法日期,比如2020-02-30会变成2020-03-01)if(date.getFullYear()==year&&(date.getMonth()+1)==parseInt(month)&&date.getDate()==parseInt(day)){return`${year}-${month}-${day}`;}else{//如果解析后日期对象与输入不一致,说明是无效日期thrownewError(`Invaliddate:${year}-${month}-${day}`);}}else{thrownewError(`Nodatefoundininput:${input}`);}}//但是,上面的验证方法在JavaScript中可能不准确,因为时区问题。而且,当输入日期非法时,Date对象会自动调整,所以我们可以用另一种方法验证:比较格式化后的字符串是否与输入一致(注意:我们输入的是本地时间,所以用本地时间方法获取)//另一种方法:不验证,直接返回组合的字符串(不推荐)//或者,我们可以用更简单的方法,不验证,因为验证在JavaScript中比较麻烦。如果不需要验证,可以这样:functionconvertDateStringWithoutValidation(input){constpattern=/(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})/;constmatch=input.match(pattern);if(match){constyear=match[1];constmonth=match[2].padStart(2,'0');constday=match[3].padStart(2,'0');return`${year}-${month}-${day}`;}else{thrownewError(`Nodatefoundininput:${input}`);}}//如果要求验证,我们可以使用第三方库,比如moment.js,但这里不引入第三方库。//因此,根据需求选择。//我们这里给出一个验证的版本,但要注意时区问题。我们假设输入的日期是本地时间的日期字符串,并且不包含时间部分。//改进验证:使用ISO格式(但时区是UTC)可能不一致,所以我们用本地时间构建,然后检查本地时间的年月日。//上面的验证版本已经做了,但是注意:月份要加1(因为getMonth返回0-11),所以比较时要注意。//但是,如果输入的是无效日期,比如2020-02-30,那么date对象会变成2020-03-01(在本地时间),这样我们比较就会发现不一致,从而抛出异常。//所以,验证版本可以工作。//但是,我们也可以不验证,因为题目只要求转换格式,并不要求验证日期。但为了健壮性(避免产生非法日期),建议验证。//因此,我们使用验证版本。//但是,在JavaScript中,由于时区问题,如果用户在不同的时区,可能会出现问题。例如,输入"2020-01-01"在UTC+8时区,用newDate创建时,是本地时间,所以getFullYear等返回的是本地时间,应该与输入一致。所以验证方法在只包含日期且没有时间的情况下是可行的。//所以,我们使用上面的验证版本。//但是,如果输入日期是"2020-01-01",而运行代码的时区是UTC-8,那么newDate("2020-01-01")会被解析为UTC时间的2020-01-01T00:00:00Z,然后转换为本地时间(UTC-8)就是2019-12-31T16:00:00,这样getFullYear返回2019,与输入不一致。//所以,为了避免时区问题,我们不要使用newDate(string)这种方式,而是使用newDate(year,monthIndex,day)这种方式,它使用本地时间,并且不会将字符串解释为UTC时间。//修改:functionconvertDateString(input){constpattern=/(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})/;constmatch=input.match(pattern);if(match){constyear=parseInt(match[1],10);constmonth=parseInt(match[2],10);//1-12constday=parseInt(match[3],10);//月份索引从0开始constdate=newDate(year,month-1,day);//验证:使用创建日期时使用的年月日来检查,因为Date会自动调整,如果日期无效,调整后的值可能不等于输入if(date.getFullYear()===year&&date.getMonth()===month-1&&date.getDate()===day){//格式化为yyyy-MM-ddconstformattedMonth=String(month).padStart(2,'0');constformattedDay=String(day).padStart(2,'0');return`${year}-${formattedMonth}-${formattedDay}`;}else{thrownewError(`Invaliddate:${year}-${month}-${day}`);}}else{thrownewError(`Nodatefoundininput:${input}`);}}//注意:这里返回的是输入的原始年月日(经过验证),而不是date对象格式化(因为date对象可能因为时区问题而改变?不会,因为我们在创建Date对象时使用了本地时间,并且验证时也是用本地时间的方法,所以如果输入日期有效,那么date.getDate()就是day,不会改变)。但是,我们返回的是原始的年月日(补零后),而不是从date对象中取。因为验证通过,说明输入日期有效,我们就可以直接使用输入的年月日来格式化。//但是,这里有一个问题:如果输入的是"2020-02-30",那么newDate(2020,1,30)会自动调整到2020-03-01(因为2月只有29天),然后验证时,date.getDate()返回1,而day=30,不相等,所以会抛出异常。//所以,这个验证是有效的。//但是,我们返回的字符串是使用原始的年月日(补零后),而不是调整后的。因为验证通过才会返回,验证不通过就抛异常。所以,返回的字符串和输入的日期一致(只是分隔符变了)。这样符合要求。//因此,这个函数可以工作。//但是,如果输入的年月日数字是字符串,我们解析成整数,然后再用这些整数创建日期,验证,然后返回时又用原始的字符串补零,这样如果输入是"0001/01/01",那么parseInt后是1,然后返回时补零成"0001-01-01"?但是,在正则匹配时,我们匹配的是4位数字,所以年一定是4位数字,但是匹配组是字符串,所以我们可以直接用字符串补零,而不用整数转换(对于月份和日,我们只需要补零到两位,而年已经固定是4位)。//修改:我们不将年月日转换成整数,而是直接使用字符串,然后补零月份和日(如果它们不是两位)。但是验证时需要整数。//所以,我们分开:验证时用整数,返回格式化时用原始字符串补零。//但为了验证,必须转换成整数。//所以,我们按上面的代码。//但是,年份我们不需要补零,因为匹配的是4位数字。月份和日需要补零。//所以,在返回时,我们用://year:已经是4位字符串//month:原始匹配的字符串,补零到2位//day:同理//但是,如果原始匹配的月份是"2",那么补零后是"02",这样返回"2020-02-...",而验证时,我们使用整数2,所以一致。//所以,代码可以写成:functionconvertDateString(input){constpattern=/(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})/;constmatch=input.match(pattern);if(match){constyearStr=match[1];constmonthStr=match[2];constdayStr=match[3];constyear=parseInt(yearStr,10);constmonth=parseInt(monthStr,10);constday=parseInt(dayStr,10);constdate=newDate(year,month-1,day);if(date.getFullYear()===year&&date.getMonth()===month-1&&date.getDate()===day){//补零constformattedMonth=monthStr.padStart(2,'0');constformattedDay=dayStr.padStart(2,'0');return`${yearStr}-${formattedMonth}-${formattedDay}`;}else{thrownewError(`Invaliddate:${yearStr}-${monthStr}-${dayStr}`);}}else{thrownewError(`Nodatefoundininput:${input}`);}}//这样,我们保留了原始字符串,只有验证时使用整数。返回时,用原始字符串补零,避免整数转换丢失前导零(比如输入"2020-01-01",匹配的月份字符串是"01",补零后还是"01",但整数是1,补零会变成"01")。//但是,如果输入是"2020-1-1",那么匹配的月份字符串是"1",补零后是"01",所以返回"2020-01-01",而验证时使用整数1,所以没问题。//所以,这个版本更好。综上所述,我们提供了Java、Python和JavaScript的解决方案。最后,根据要求,在回答的最后提出相关问题。注意:相关问题需要4个。</think>### 健壮的日期格式转换方案 要将 `yyyy/MM/dd` 格式的字符串转换为 `yyyy-MM-dd` 格式,并确保在分隔符变化时仍然有效,最健壮的方法是**提取日期数字部分后重新组合**,而不是直接替换分隔符。以下是几种语言的实现方案: #### 核心思路 1. **正则表达式提取数字部分**:匹配连续的数字序列(年、月、日) 2. **格式化工位**:确保月和日总是两位数(补零) 3. **重新组合**:用目标分隔符连接 --- ### Java 实现(使用正则表达式) ```java import java.util.regex.Matcher; import java.util.regex.Pattern; public class DateConverter { public static String convertDate(String input) { // 匹配4位年 + 分隔符 + 1-2位月 + 分隔符 + 1-2位日 Pattern pattern = Pattern.compile("(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})"); Matcher matcher = pattern.matcher(input); if (matcher.find()) { String year = matcher.group(1); String month = String.format("%02d", Integer.parseInt(matcher.group(2))); String day = String.format("%02d", Integer.parseInt(matcher.group(3))); return year + "-" + month + "-" + day; } else { throw new IllegalArgumentException("Invalid date format: " + input); } } // 测试用例 public static void main(String[] args) { System.out.println(convertDate("2023/05/15")); // 2023-05-15 System.out.println(convertDate("2023-05-15")); // 2023-05-15 System.out.println(convertDate("2023年05月15日")); // 2023-05-15 System.out.println(convertDate("2023.05.15")); // 2023-05-15 } } ``` **代码说明**: 1. 正则表达式 `(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})`: - `(\\d{4})`:匹配4位年份 - `[^\\d]+`:匹配任意非数字分隔符(/、-、.、中文等) - `(\\d{1,2})`:匹配1-2位的月/日 2. `String.format("%02d", ...)`:确保月和日总是两位数(不足补零) 3. 此方案不依赖固定分隔符,兼容各种变体 --- ### Python 实现 ```python import re def convert_date(input_str): match = re.search(r'(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})', input_str) if match: year, month, day = match.groups() return f"{year}-{int(month):02d}-{int(day):02d}" else: raise ValueError(f"Invalid date format: {input_str}") # 测试 print(convert_date("2023/05/15")) # 2023-05-15 print(convert_date("2023-05-15")) # 2023-05-15 print(convert_date("2023年05月15日")) # 2023-05-15 print(convert_date("2023.05.15")) # 2023-05-15 ``` **代码说明**: - `r'(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})'`:与Java相同的正则逻辑 - `f"{int(month):02d}"`:直接格式化整数为两位数 - 异常处理确保无效格式被捕获 --- ### JavaScript 实现 ```javascript function convertDate(input) { const match = input.match(/(\d{4})[^\d]+(\d{1,2})[^\d]+(\d{1,2})/); if (match) { const [, year, month, day] = match; const pad = (num) => num.padStart(2, '0'); return `${year}-${pad(month)}-${pad(day)}`; } throw new Error(`Invalid date format: ${input}`); } // 测试 console.log(convertDate("2023/05/15")); // "2023-05-15" console.log(convertDate("2023-05-15")); // "2023-05-15" console.log(convertDate("2023年05月15日")); // "2023-05-15" console.log(convertDate("2023.05.15")); // "2023-05-15" ``` **代码说明**: - `padStart(2, '0')`:字符串补零到两位 - 解构赋值 `[, year, month, day]` 跳过完整匹配项 - 兼容浏览器和Node.js环境 --- ### 方案优势 1. **分隔符无关**:处理 `/`、`-`、`.`、中文等任意分隔符 2. **自动补零**:单数字月份/日自动补零(5 → 05) 3. **格式验证**:无效日期格式会抛出异常 4. **无依赖**:纯正则实现,无需额外库 > **注意**:此方案假设日期顺序始终为 **年→月→日**。如果输入顺序可能变化(如美式日期),需要额外处理。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值