JS 如何判断字符串中包含代码

18 min read
function includeCode(text: string | null | undefined) {
  const regexp = /^(?:\s{4}|\t).+/gm
  return !!(text?.includes(' = ') || text?.match(regexp))
}

正则表达式的定义为 /^(?:\s{4}|\t).+/gm,其中:

  • /^ 表示匹配字符串的开头
  • (?:\s{4}|\t) 表示匹配四个空格或一个制表符(\t
  • . 表示匹配任意字符(除了换行符)
  • + 表示匹配前面的字符一次或多次
  • /gm 表示全局和多行匹配模式

因此,该正则表达式可以匹配每行以四个空格或一个制表符开始的任意字符。这些行被认为是代码行。

  • 该正则表达式不能判断代码块开头的空行是否为代码行。如果代码块以空行开头,则该空行不包含四个空格或一个制表符,不符合正则表达式的匹配条件,因此不会被认为是代码行。但是实际上,该空行可能是代码块的一部分,因此不应该被过滤掉。
  • 该正则表达式不能判断注释行是否为代码行。如果代码中包含注释,则注释行同样需要被处理。但是该正则表达式只能匹配以四个空格或一个制表符开头的行,无法匹配以 ///* 开头的注释行。
  • 该正则表达式可能会将非代码行错误地匹配为代码行。例如,如果一行的开头包含了四个空格或一个制表符,但不是代码,而是缩进的普通文本,那么该行仍然会被认为是代码行。
  • 该正则表达式不能处理多种缩进方式。该正则表达式只能匹配以四个空格或一个制表符开头的行,无法处理其他缩进方式,例如两个空格或八个空格。

一个可以供测试的代码判断正则如下

function includeCode(text) {
  // 定义代码行的正则表达式
  const codeRegex = /((?:[^\\:]|^)\/\/.*$)|(\/\*[\s\S]*?\*\/)|(^|\n)[ \t]*([a-zA-Z_$][0-9a-zA-Z_$]*)(\s*[:=].*?)?([\n\r;]|\/\/|$)/gm;
  // 判断文本中是否包含代码
  return codeRegex.test(text);
}

该实现方式的主要思路是使用正则表达式来判断文本中是否包含代码。代码行的正则表达式 codeRegex 中,除了匹配以四个空格或一个制表符开头的代码行以外,还添加了一些规则,例如:

  • 匹配以 // 开头的单行注释和以 /* */ 包含的多行注释。
  • 使用较为严格的命名规则来匹配变量名,例如必须以字母、下划线或 $ 开头,后面可以接任意数量的字母、数字、下划线或 $
  • 尝试匹配变量的赋值或定义,例如使用 =: 进行赋值或定义。
  • 匹配行末的换行符、回车符、分号或注释符。

非捕获组(non-capturing group)的语法

(?:pattern) 是一个非捕获组(non-capturing group)的语法,表示匹配 pattern 但不捕获它。相比于普通的括号表达式 (pattern),非捕获组的作用在于将括号中的内容看做一个整体,并不创建对应的匹配组(matching group),因此不会对匹配结果造成影响。这种语法在使用正则表达式进行文本匹配时非常有用。

在上面的正则表达式 ^(?:\s{4}|\t).+/gm 中,(?:\s{4}|\t) 表示匹配四个空格或一个制表符(\t),并且不会将这个部分作为匹配组进行捕获。也就是说,这个括号中的内容只是为了和 | 运算符一起构成一个可以匹配四个空格或一个制表符的正则表达式子表达式,并不会保存匹配到的结果。这样做的好处是,即使在匹配到包含四个空格或一个制表符的行时,不会将这个部分的结果保存下来,而是只会将整行作为匹配结果进行保存,从而方便后续处理。