1.同20章 线性表、栈、队列和优先队列的第10题。
2.同20章 线性表、栈、队列和优先队列的第1题。
3.修改程序清单21-7中的程序。如果关键字在注释或者字符串中,则不进行统计。将Java文件名从命令行传递。
import java.io.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static final String[] keyWordsArray = {"abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while"};
public static final Set<String> keyWordSet = new HashSet<>(Arrays.asList(keyWordsArray));
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("命令行参数有误!");
return;
}
int r = countKeyWords(new File(args[0]));
if (r == -1)
System.err.println("文件打开失败!");
else
System.out.println("有" + r + "个Java关键字!");
}
public static int countKeyWords(File file) {
int count = 0;
try (FileReader reader = new FileReader(file)) {
int c;
boolean lineNote = true, blockNote = true;
StringBuilder sb = new StringBuilder();
while ((c = reader.read()) != -1)
switch ((char) c) {
case '/':
if ((c = reader.read()) == -1) return count;
if ((char) c == '/') lineNote = false;
else if ((char) c == '*') blockNote = false;
if (keyWordSet.contains(sb.toString())) ++count;
sb.setLength(0);
break;
case '\n':
if (!lineNote) {
lineNote = true;
break;
}
case ' ':
case '\t':
if (keyWordSet.contains(sb.toString())) ++count;
sb.setLength(0);
break;
case '*':
if ((c = reader.read()) == -1) return count;
if ((char) c == '/') blockNote = true;
break;
default:
if (lineNote && blockNote) sb.append((char) c);
}
} catch (IOException e) {
return -1;
}
return count;
}
}
4.编写一个程序,提示用户输入一个文本文件名,然后显示文件中的元音和辅音的数目。使用规则集存储元音A
、E
、I
、O
和U
。
public static void main(String[] args) {
Map<Character, Integer> map = new HashMap<>(Map.of('A', 0, 'E', 0, 'I', 0, 'O', 0, 'U', 0));
System.out.println("请输入文件名:");
Scanner scanner = new Scanner(System.in);
String fileName = scanner.next();
scanner.close();
try (FileReader reader = new FileReader(fileName)) {
int c;
while ((c = reader.read()) != -1)
switch ((char) c) {
case 'A':
case 'a':
map.put('A', map.get('A') + 1);
break;
case 'E':
case 'e':
map.put('E', map.get('E') + 1);
break;
case 'I':
case 'i':
map.put('I', map.get('I') + 1);
break;
case 'O':
case 'o':
map.put('O', map.get('O') + 1);
break;
case 'U':
case 'u':
map.put('U', map.get('U') + 1);
}
map.forEach((k, v) -> System.out.println("字母" + k + "出现了" + v + "次"));
} catch (IOException e) {
System.err.println("文件打开失败!");
}
}
5.编写一个程序,将一个Java程序转换为一个HTML文件。在HTML文件中,关键字、注释和字面量分别用粗体的深蓝色、绿色和蓝色显示。使用命令行传递Java文件和HTML文件。例如,下面的命令
java Exercise21_05 Welcome.java Welcome.html
将Welcome.java转换为Welcome.html。下图a显示了一个Java文件,它对应的HTML文件如图b所示。
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Set;
public class JavaToHtmlConverter {
public static final Set<String> KEYWORDS = Set.of("abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while");
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("命令行参数有误!");
return;
}
try {
convert(args[0], args[1]);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void convert(String javaFilePath, String htmlFilePath) throws IOException {
FileReader reader = new FileReader(javaFilePath);
FileWriter writer = new FileWriter(htmlFilePath);
writer.write("<!DOCTYPE html>\n");
writer.write("<html>\n");
writer.write("<head>\n");
writer.write("<title>Java Source Code as HTML</title>\n");
writer.write("<style>\n");
writer.write(".keyword { color: darkblue; font-weight: bold; }\n");
writer.write(".comment { color: green; }\n");
writer.write(".literal { color: blue; }\n");
writer.write("</style>\n");
writer.write("</head>\n");
writer.write("<body>\n");
writer.write("<pre>\n");
int c;
StringBuilder sb = new StringBuilder();
boolean lineNote = false, blockNote = false;
String s;
while ((c = reader.read()) != -1)
switch ((char) c) {
case '/':
if (lineNote || blockNote) {
writer.write((char) c);
break;
}
if ((c = reader.read()) == -1) {
writer.write(sb.toString());
writer.write("/\n</pre>\n</body>\n</html>");
reader.close();
writer.close();
return;
}
if (!sb.isEmpty() && sb.charAt(sb.length() - 1) == '\'' && (char) c == '\'') {
sb.deleteCharAt(sb.length() - 1);
s = sb.toString();
if (!s.isEmpty())
if (KEYWORDS.contains(s)) {
writer.write("<span class=\"keyword\">");
writer.write(s);
writer.write("</span>");
} else if (isLiteral(s)) {
writer.write("<span class=\"literal\">");
writer.write(s);
writer.write("</span>");
} else
writer.write(s);
writer.write("<span class=\"literal\">'/'</span>");
break;
}
s = sb.toString();
if ((char) c == '/') {
if (KEYWORDS.contains(s)) {
writer.write("<span class=\"keyword\">");
writer.write(s);
writer.write("</span> <span class=\"comment\">//");
lineNote = true;
sb.setLength(0);
break;
}
if (!s.isEmpty()) writer.write(s);
writer.write("<span class=\"comment\">//");
lineNote = true;
break;
}
if ((char) c == '*') {
if (KEYWORDS.contains(s)) {
writer.write("<span class=\"keyword\">");
writer.write(s);
writer.write("</span> <span class=\"comment\">");
blockNote = true;
sb.setLength(0);
break;
}
if (!s.isEmpty()) writer.write(s);
writer.write("<span class=\"comment\">/*");
blockNote = true;
break;
}
if (!s.isEmpty()) writer.write(s);
writer.write('/');
writer.write(c);
break;
case '\"':
if (lineNote || blockNote || !sb.isEmpty()) {
writer.write((char) c);
break;
}
sb.append('\"');
while ((char) (c = reader.read()) != '\"') {
if (c == -1) {
writer.write(sb.toString());
writer.write("\n</pre>\n</body>\n</html>");
reader.close();
writer.close();
return;
}
if (!sb.isEmpty() && sb.charAt(sb.length() - 1) == '/') {
if ((char) c == '/') {
sb.deleteCharAt(sb.length() - 1);
writer.write(sb.toString());
sb.setLength(0);
writer.write("<span class=\"comment\">//");
lineNote = true;
break;
}
if ((char) c == '*') {
sb.deleteCharAt(sb.length() - 1);
writer.write(sb.toString());
sb.setLength(0);
writer.write("<span class=\"comment\">/*");
blockNote = true;
break;
}
}
sb.append((char) c);
}
if (!lineNote && !blockNote) {
writer.write("<span class=\"literal\">");
writer.write(sb.toString());
sb.setLength(0);
writer.write("\"</span>");
}
break;
case '*':
if (!blockNote) {
sb.append('*');
break;
}
if ((c = reader.read()) == -1) {
writer.write(sb.toString());
writer.write("*\n</pre>\n</body>\n</html>");
reader.close();
writer.close();
return;
}
if ((char) c == '/') {
blockNote = false;
writer.write("*/</span>");
break;
}
writer.write('*');
writer.write('/');
break;
case '\n':
if (lineNote) {
writer.write(sb.toString());
sb.setLength(0);
writer.write("</span>\n");
lineNote = false;
break;
}
case ' ':
case '\t':
case '(':
case '[':
case '{':
case ';':
case ':':
case ')':
case ']':
case '}':
case '.':
case ',':
case '\r':
case '=':
case '+':
case '-':
case '&':
case '|':
case '~':
case '^':
case '!':
case '<':
case '>':
case '?':
s = sb.toString();
if (s.isEmpty()) {
writer.write(c);
break;
}
if (lineNote || blockNote) {
writer.write(s);
writer.write(c);
sb.setLength(0);
break;
}
if (KEYWORDS.contains(s)) {
writer.write("<span class=\"keyword\">");
writer.write(s);
writer.write("</span>");
} else if (isLiteral(s)) {
writer.write("<span class=\"literal\">");
writer.write(s);
writer.write("</span>");
} else
writer.write(s);
sb.setLength(0);
writer.write(c);
break;
default:
if (lineNote || blockNote) {
writer.write(c);
break;
}
sb.append((char) c);
}
s = sb.toString();
if (lineNote || blockNote) {
writer.write(s);
writer.write("</span>");
} else if (KEYWORDS.contains(s)) {
writer.write("<span class=\"keyword\">");
writer.write(s);
writer.write("</span>");
} else if (isLiteral(s)) {
writer.write("<span class=\"literal\">");
writer.write(s);
writer.write("</span>");
} else
writer.write(s);
writer.write("\n</pre>\n</body>\n</html>");
reader.close();
writer.close();
}
// 判断是否是字符或者数字
private static boolean isLiteral(String word) {
return word.matches("'.'") || word.matches("[-+]?(?:\\b\\d+(?:\\.\\d*)?(?:[eE][-+]?\\d+)?[fF]?|[lL]?\\b\\d+\\b)");
}
}
6.编写一个程序,读取不确定个数的整数,然后查找其中出现频率最高的数字。当输入为0时,表示结束输入。例如,如果输入的数据是2 3 40 3 5 4 -3 3 3 2 0,那么数字3的出现频率是最高的。如果出现频率最高的数字不是一个而是多个,则应该将它们全部报告。例如,在线性表9 30 3 9 3 2 4中,3和9都出现了两次,所以3和9都应该被报告。
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
HashMap<Integer, Integer> map = new HashMap<>();
LinkedList<Integer> maxNumList = new LinkedList<>();
Scanner scanner = new Scanner(System.in);
int x, maxNum = 0;
while ((x = scanner.nextInt()) != 0) {
int v;
if (map.containsKey(x)) {
v = map.get(x);
map.put(x, ++v);
} else
map.put(x, v = 1);
if (v > maxNum) {
maxNum = v;
maxNumList.clear();
maxNumList.push(x);
} else if (v == maxNum)
maxNumList.push(x);
}
scanner.close();
System.out.println(maxNumList);
}
}
7.改写程序清单21-9,将单词按出现次数的升序显示。
import java.util.*;
public class Test {
public static void main(String[] args) {
String text = "Good morning. Have a good class. Have a good visit. Have fun!";
String[] words = text.split("[\\s+\\p{P}]");
HashMap<String, Integer> map = new HashMap<>();
for (String word : words)
if (!word.isEmpty()) {
String s = word.toLowerCase();
map.put(s, map.getOrDefault(s, 0) + 1);
}
ArrayList<Map.Entry<String, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
for (Map.Entry<String, Integer> entry : list) System.out.println(entry.getKey() + '\t' + entry.getValue());
}
}
8.改写程序清单21-9,从文本文件中读取文本,文本文件名作为命令行参数传递。单词由空格、标点符号(,;.:?)、引号('")以及括号分隔。单词计数时不区分大小写(例如,认为Good和good是一样的单词)。单词必须以字母开头。以单词的字母顺序显示输出,每个单词前面显示其出现次数。
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.BiConsumer;
public class Test {
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.err.println("命令行参数不正确!");
return;
}
FileReader reader = new FileReader(args[0]);
Map<String, Integer> map = new TreeMap<>();
int c;
StringBuilder sb = new StringBuilder();
String s;
while ((c = reader.read()) != -1)
switch ((char) c) {
case ' ':
case ',':
case ';':
case '.':
case ':':
case '?':
case '\'':
case '"':
case '(':
case '[':
case '{':
case ')':
case ']':
case '}':
case '\n':
case '\r':
if (sb.isEmpty()) break;
if (!Character.isLetter(sb.charAt(0))) { // 认为只要以字母开头都是“单词”
sb.setLength(0);
break;
}
s = sb.toString().toLowerCase();
sb.setLength(0);
map.put(s, map.getOrDefault(s, 0) + 1);
break;
default:
sb.append((char) c);
}
reader.close();
map.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String s, Integer i) {
System.out.println(String.valueOf(i) + '\t' + s);
}
});
}
}
9.改写编程练习题8.37,在映射中存储州和其首府的条目。你的程序应该提示用户输入一个州,然后显示该州的首府。
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Test {
public static Map<String, String> provincialCapital;
public static void main(String[] args) {
getData();
System.out.println("请输入省份:");
Scanner scanner = new Scanner(System.in);
String s = scanner.next();
scanner.close();
if (provincialCapital.containsKey(s))
System.out.printf("%s的省会是%s。", s, provincialCapital.get(s));
else
System.out.printf("不存在省会%s!", s);
}
private static void getData() {
provincialCapital = new HashMap<>(29, 1f);
provincialCapital.put("河北", "石家庄");
provincialCapital.put("山西", "太原");
provincialCapital.put("辽宁", "沈阳");
provincialCapital.put("吉林", "长春");
provincialCapital.put("黑龙江", "哈尔滨");
provincialCapital.put("江苏", "南京");
provincialCapital.put("浙江", "杭州");
provincialCapital.put("安徽", "合肥");
provincialCapital.put("福建", "福州");
provincialCapital.put("江西", "南昌");
provincialCapital.put("山东", "济南");
provincialCapital.put("河南", "郑州");
provincialCapital.put("湖北", "武汉");
provincialCapital.put("湖南", "长沙");
provincialCapital.put("广东", "广州");
provincialCapital.put("海南", "海口");
provincialCapital.put("四川", "成都");
provincialCapital.put("贵州", "贵阳");
provincialCapital.put("云南", "昆明");
provincialCapital.put("陕西", "西安");
provincialCapital.put("甘肃", "兰州");
provincialCapital.put("青海", "西宁");
provincialCapital.put("台湾", "台北");
provincialCapital.put("内蒙古", "呼和浩特");
provincialCapital.put("广西", "南宁");
provincialCapital.put("西藏", "拉萨");
provincialCapital.put("宁夏", "银川");
provincialCapital.put("新疆", "乌鲁木齐");
}
}
10.重写程序清单21-7,读入一个Java源代码文件并且统计文件中每个关键字的出现次数。如果关键字是在注释中或者字符串字面值中,则不要进行统计。
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class Test {
public static final Set<String> keywords = Set.of("abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while");
private static final Map<String, Integer> map = new TreeMap<>();
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.err.println("命令行参数不正确!");
return;
}
int c;
StringBuilder sb = new StringBuilder();
String s;
FileReader reader = new FileReader(args[0]);
// 第1位:是否是行注释
// 第2位:是否是块注释
// 第3位:是否是字符串
byte isValid = 0;
while ((c = reader.read()) != -1)
switch ((char) c) {
case '/':
if (isValid != 0) break; // 不考虑字符串还未结束就开始行注释以及行注释中开始块注释的情况 (语法错误!)
if ((c = reader.read()) == -1) {
if (!sb.isEmpty()) {
s = sb.toString();
if (keywords.contains(s)) map.put(s, map.getOrDefault(s, 0) + 1);
}
printKeywordFrequency();
return;
}
isValid = switch ((char) c) {
case '/' -> 1;
case '*' -> 2;
default -> 0;
};
s = sb.toString();
sb.setLength(0);
if (keywords.contains(s)) map.put(s, map.getOrDefault(s, 0) + 1);
break;
case '"':
if (isValid == 4) {
isValid = 0;
break;
}
if (isValid != 0) break;
isValid = 4;
s = sb.toString();
sb.setLength(0);
if (keywords.contains(s)) map.put(s, map.getOrDefault(s, 0) + 1);
break;
case '\n':
if (isValid == 1) {
isValid = 0;
break;
}
case ' ':
case '\t':
case '\r':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case ',':
case '.':
case ';':
case '=':
case '~':
case '+':
case '-':
case '?':
case '!':
case '^':
case '&':
case '|':
case '<':
case '>':
if (isValid != 0) break;
if (sb.isEmpty()) break;
s = sb.toString();
if (keywords.contains(s)) map.put(s, map.getOrDefault(s, 0) + 1);
sb.setLength(0);
break;
case '*':
if (isValid == 0) {
sb.append('*');
break;
}
if (isValid != 2) break;
if ((c = reader.read()) == -1) {
if (!sb.isEmpty()) {
s = sb.toString();
if (keywords.contains(s)) map.put(s, map.getOrDefault(s, 0) + 1);
}
printKeywordFrequency();
return;
}
if ((char) c == '/') isValid = 0;
break;
default:
if (isValid == 0) sb.append((char) c);
}
reader.close();
if (!sb.isEmpty()) {
s = sb.toString();
if (keywords.contains(s)) map.put(s, map.getOrDefault(s, 0) + 1);
}
printKeywordFrequency();
}
private static void printKeywordFrequency() {
if (map.isEmpty())
System.out.println("不存在关键字!");
else
map.forEach((s, i) -> System.out.printf("关键字%s出现了%d次。\n", s, i));
}
}
14.重写程序清单12.18,为ListOfPendingURLs
和listOfTraversedURLs
采用合适的新数据结构以提高性能。
import java.io.IOException;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个地址:");
String url = scanner.nextLine();
scanner.close();
WebCrawler crawler = new WebCrawler() {
@Override
public void eachURL(String url) {
System.out.println("Crawl: " + url);
}
};
crawler.crawl(url);
}
}
import java.io.IOException;
import java.net.URL;
import java.util.*;
public abstract class WebCrawler {
public void crawl(String rootURL) throws IOException {
LinkedList<String> pendingURLs = new LinkedList<>();
HashSet<String> traversedURLs = new HashSet<>();
pendingURLs.add(rootURL);
while (!pendingURLs.isEmpty() && traversedURLs.size() < 101) {
String url = pendingURLs.poll();
if (!traversedURLs.contains(url)) {
traversedURLs.add(url);
eachURL(url);
getSubURLs(url, pendingURLs, traversedURLs);
}
}
}
public abstract void eachURL(String url);
// 将所有的子地址插入到列表末尾,并排除exclusionSet
private static void getSubURLs(String rootURL, List<String> list, Set<String> exclusionSet) throws IOException {
URL url = new URL(rootURL);
Scanner scanner = new Scanner(url.openStream());
int currentIndex = 0;
while (scanner.hasNext()) {
String s = scanner.nextLine();
currentIndex = s.indexOf("http:", currentIndex);
while (currentIndex > 0) {
int endIndex = s.indexOf('"', currentIndex);
if (endIndex > 0) {
String currentURL = s.substring(currentIndex, endIndex);
if (exclusionSet == null || !exclusionSet.contains(currentURL)) list.add(currentURL);
currentIndex = s.indexOf("http:", endIndex);
} else
currentIndex = -1;
}
}
}
// 程序清单12-18的等价方法
public static ArrayList<String> getSubURLs(String rootURL) throws IOException {
ArrayList<String> list = new ArrayList<>();
getSubURLs(rootURL, list, null);
return list;
}
}