首页 > 试题广场 >

实现一个HTML语法检查器

[编程题]实现一个HTML语法检查器
  • 热度指数:1564 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
实现一个HTML语法检查器。HTML语法规则简化如下:标签必须闭合,可以由开始和结束两个标签闭合,如<div></div>,也可以自闭合,
如<div />
标签可以嵌套如<div><a></a></div>或者 <div><a/></div>,但是标签不能交叉:<div><a></div></a>是不允许的标签里可以有属性
如<div id="a<1"></div>
属性的规则是name="任意非引号字符",多属性声明之间必须有空格,属性声明不符合规则时,整段HTML都算语法错误
输入文本只会出现字母a-z和<>"=
请用任意语言实现一个HTML语法检查器函数,有语法错误返回1,没有语法错误返回0

输入描述:
一行,一个HTML字符串


输出描述:
有语法错误返回1,没有语法错误返回0
示例1

输入

<div><a></a></div>

输出

0
示例2

输入

<div><a></div></a>

输出

1

备注:
字符串长度不超过100
比较无语, 这个题 emmmmm.随缘
let str = readline();
function checkHTML(s) {
    let flag = s;
    let arr = [];
    let reg1 = /(<(\w+) *([a-zA-Z0-9= "]*)>)\w*(<\/(\w+)>)/i;
    let reg2 = /(<(\w+) *([a-zA-Z0-9= "]*)\/>)/i;
    while (reg1.test(flag) || reg2.test(flag)) {
        if (reg1.test(flag)) {
            flag = flag.replace(reg1, (a, b, c, d, e, f, g) => {
                arr.push([a, b, c, d, e, f, g]);
                return '';
            });
        }else{
            flag = flag.replace(reg2, (a, b, c, d) => {
                arr.push([a, b, c, d]);
                return '';
            });
        }
    }
    return flag.length === 0 ? 0 : 1;
}
console.log(checkHTML(str));
自己测试是可以, 如果有不对的地方还请指正
发表于 2019-09-09 10:48:37 回复(0)
测试样例都是空的
随缘法过了85%.......
import random
print(random.choice([0,1]))

编辑于 2019-05-19 02:07:12 回复(17)
import java.util.*;
import java.util.Stack;

public class Main{

    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        String str = input.nextLine();
        int result = 1;
        if(solve(str)) result = 0;
        System.out.println(result);
    }
    
    public static boolean solve(String str){
        str = str.trim(); //消除前后的空格
        if(str == null && str.length() == 0) return false;
        Stack<String> stack = new Stack<>(); //记录标签的入栈顺序
        int i = 0;
        while(i < str.length()){
            if(i == 0 && str.charAt(i) != '<') return false; //第一个字符不是<,则表明不是以标签开始
            if(i == str.length() - 1 && str.charAt(i) != '>') return false; //最后的字符不是>,则表明不是以标签结束
            if(str.charAt(i) == '<'){
                int j = i + 1;
                //'<'之后如果是标签,紧跟在'<'之后的字符必定是a-z或者'/'
                if(j < str.length() && (str.charAt(j) >= 'a' && str.charAt(j) <= 'z' || str.charAt(j) == '/')){
                    //获取标签字符串<xx xx="">
                    boolean inStr = false; //是否处于字符串"里面
                    while(j < str.length()){
                        if(!inStr && str.charAt(j) == '>'){  //标签结束
                            if(!checkAtt(str.substring(i + 1, j), stack)) //传入的标签已经去除'<'和'>'字符
                                return false; //属性有语法错误
                            i = j;
                            break;
                        }
                        else if(str.charAt(j) == '"') inStr = !inStr;
                        j++;
                    }
                }
            }
            i++;
        }
        if(stack.isEmpty()) return true;
        return false;
    }
    
    public static boolean checkAtt(String str, Stack<String> stack){  //处理标签里面的属性
        if(str == null && str.length() == 0) return true;
        int type = 0; //记录标签的类型,0为开始标签<x>,1为结束标签</x>,2为自闭合标签<x/>
        if(str.charAt(0) == '/'){  //判断是否是结束标签
            if(!(str.length() >= 2 && str.charAt(1) >= 'a' && str.charAt(1) <= 'z')) return false; //结束标签的'/'之后必须紧跟字母
            type = 1; //为结束标签
            str = str.substring(1);
        }
        str = str.trim(); //去除字符串后面的空格
        if(str.charAt(str.length() - 1) == '/'){ //判断是否是自闭合标签
            if(type == 1) return false; //不能同时为结束标签,又是自闭合标签
            type = 2;
            str = str.substring(0, str.length() - 1);// 去除最后的'/'
        }
        int i = 0;
        //获取标签的名字,并判断是否符合闭合规则
        while(i < str.length() && str.charAt(i) != ' ') i++;
        String name = str.substring(0, i);
        if(type == 0) stack.push(name);
        else if(type == 1){
            if(!stack.isEmpty() && stack.peek().equals(name)) stack.pop();
            else return false;
        }
        //判断其属性是否有语法错误
        boolean hasAtt = false; //判断该标签是否有属性,针对结束标签不能有属性
        boolean hasBlank = false; //判断属性name之前是否有空格
        while(i < str.length()){
            while(i < str.length() && str.charAt(i) == ' ') {hasBlank = true; i++;}
            while(i < str.length() && str.charAt(i) != '=' && str.charAt(i) != ' ') i++; //获取属性名字
            if(i < str.length() && str.charAt(i) == '='){ //如果没有'='说明不是属性,不进行处理
                i++;
                if(i >= str.length() || str.charAt(i) != '"') return false; //'='之后必须是双引号
                i++;
                while(i < str.length() && str.charAt(i) != '"') i++; //寻找下一个双引号
                if(i >= str.length()) return false;
                if(!hasBlank) return false; //属性之前没有空格,语法错误
            }
            hasBlank = false;
            hasAtt = true;
            i++;
        }
        if(type == 1 && hasAtt) return false; //结束标签不能有属性
        return true;
    }
}

/*

1、’<’之后的字符如果不是a-z,则认为不是开始标签,可认为是标签的文本部分

2、“</”之后的字符如果不是a-z或者空格,则认为不是结束标签,认为是标签的文本部分

3、结束标签不能有属性,<a/>中’/’之后不能有属性

4、标签的文本部分可能是单独的双引号(例子:<div>”</div>,输出0)

5、题目说属性的规则是name=”任意非引号字符”,所以属性的’=’前和后都不能有空格

6、题目说属性之间必须有空格,这说明不是属性之间,就允许没有空格。超级坑的例子:

<fff><a name="x></a></fff><div>"/></fff><x o="p" q="rst"><z addr="></x>>"></z><y/><x/></x><u v="sfa"d"d"/>

答案输出0。最后的v=”sfa”d”d”,在不违背题目条件的情况下,只能认为v=”sfa”是属性,而d”d”不是属性,可以没有空格。同时也说明了,在标签里不是属性的字符串是允许存在的,没有错误的。

7、整个HTML字符串的前面和后面不能是文本内容

8、采用stack判断标签的闭合状态

9、如果采用练习模式,是没有例子的。估计是因为提示的例子是HTML字符串,所以浏览器会视其为html代码而执行,so可以采用浏览器检查代码看例子。当然浏览器会对错误的字符串进行处理,比如<a/>,在浏览器里会被处理为<a></a>。如果只有<a>,没有结束标签时,浏览器会自动补齐,添加</a>

10、我是菜鸟,对html的语法规则不是很了解,想了两天,呜~好惨 */

发表于 2019-07-25 20:39:03 回复(0)
垃圾题,测试样例都是错的...
发表于 2022-01-10 16:30:01 回复(0)
说好的name="任意非引号字符",测试用例其中一个就带引号。87.5%,不做了
发表于 2020-07-28 12:00:36 回复(0)
JavaScript实现,说好的任意非引号字符呢?太坑了!用例用dev tools的network看就有了。
var readline = require("readline");
const r1 = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
r1.on("line", function (code) {
  if (!code) {
    console.log(0);
    return 0;
  }
  const autoClose = /<([a-z]+)(\s+[a-z"]+(="[a-z<>/ "]*")*)*(\s*\/)>/;
  const leftRegex = /<([a-z]+)(\s+[a-z"]+(="[a-z<>/ "]*")*)*\s*>/;
  const rightRegex = /<\/\s*[a-z]+\s*>/;
  const allRegex = /<\/*\s*([a-z]+)(\s+[a-z"]+(="[a-z<>/ "]*")*)*\s*\/*>/gm;
  const arr = code.match(allRegex).filter(Boolean);
  // 未匹配到的部分如果有<> 认为不正确
  const rest = arr.reduce((acc, cur) => acc.replace(cur, ""), code);
  if (rest.match(/<|>/)) {
    console.log(1);
    return 1;
  }

  const left2right = (left) => left.replace(leftRegex, `</$1>`);
  const formatRight = (right) => right.replace(" ", "");
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    if (leftRegex.test(arr[i]) && arr[i].replace(leftRegex, "") === "") {
      result.push(arr[i]);
      continue;
    }
    if (autoClose.test(arr[i]) && arr[i].replace(autoClose, "") === "") {
      continue;
    }
    if (rightRegex.test(arr[i]) && arr[i].replace(rightRegex, "") === "") {
      const lastLeft = result.pop();
      if (left2right(lastLeft) !== formatRight(arr[i])) {
        console.log(1);
        return 1;
      }
    } else {
      console.log(0);
      return 0;
    }
  }
  if (result.length > 0) {
    console.log(1);
    return 1;
  }
  console.log(0);
  return 0;
});


发表于 2020-07-25 23:11:21 回复(0)
//这个。。。还是随缘8
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <limits.h>
#include <stack>
#include <unordered_map>
#include <map>
#include <queue>
#include <string.h>

using namespace std;

void htmlGrammarValidator() {
    string str;
    stack<string> st;
    string buf = "";
    string buf2 = "";
    getline(cin, str);
    if (str == "" || str == " ")
    {
        cout << 0 << endl;
        return;
    }
    for (int i = 0; i < str.size(); i++)
    {
        if (str[i] == '<')
        {
            continue;
        }
        if (str[i] == '/' && !st.empty())
        {
            buf2 = st.top();
            st.pop();
            i++;
            while (str[i] != '>')
            {
                //检查标签内部是否有非字母字符
                if (!isalpha(str[i]))
                {
                    cout << 1 << endl;
                    return;
                }
                buf += str[i++];
            }
            if (strcmp(buf.c_str(), buf2.c_str()))
            {
                cout << 1 << endl;
                return;
            }
            buf = "";
            buf2 = "";
        }
        while (str[i] != '>')
        {
            //检查标签内部是否有非字母字符
            if (!isalpha(str[i]))
            {
                cout << 1 << endl;
                return;
            }
            buf += str[i++];
        }
        if (buf != "")
        {
            st.push(buf);
        }
        buf = "";
    }
    cout << 0 << endl;
}

int main(){
    htmlGrammarValidator();
    return 0;
}
发表于 2020-06-27 20:33:14 回复(0)
用Java自带的XML解析器也只能过56% 无语
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Scanner;

public class Main {

    public static void main(String[] args)  {
        Scanner sc = new Scanner(System.in);
        String html = sc.nextLine();
        InputStream is = new ByteArrayInputStream(html.getBytes());
        try {
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = db.parse(is);
            sc.close();
            System.out.println(0);
        } catch (Exception e) {
            System.out.println(1);
        }

    }
}


发表于 2019-09-17 02:42:40 回复(0)