本机工具可以更快地处理此类文本文件的主要原因是它们假定一个特定的字符集,尤其是当它具有基于ASCII的8位编码,而Java执行字节到字符的转换时,其抽象能力可以支持任意字符集。
当我们类似地假设具有上述属性的单个字符集时,我们可以使用低级工具,这些工具可能会大大提高性能。
对于此类操作,我们定义了以下辅助方法:
private static char[] getTable(Charset cs) { if(cs.newEnprer().maxBytesPerChar() != 1f) throw new UnsupportedOperationException("Not an 8 bit charset"); byte[] raw = new byte[256]; IntStream.range(0, 256).forEach(i -> raw[i] = (byte)i); char[] table = new char[256]; cs.newDeprer().onUnmappableCharacter(CodingErrorAction.REPLACE) .depre(ByteBuffer.wrap(raw), CharBuffer.wrap(table), true); for(int i = 0; i < 128; i++) if(table[i] != i) throw new UnsupportedOperationException("Not ASCII based"); return table;}和
private static CharSequence mapAsciibasedText(Path p, char[] table) throws IOException { try(FileChannel fch = FileChannel.open(p, StandardOpenOption.READ)) { long actualSize = fch.size(); int size = (int)actualSize; if(size != actualSize) throw new UnsupportedOperationException("file too large"); MappedByteBuffer mbb = fch.map(FileChannel.MapMode.READ_ONLY, 0, actualSize); final class MappedCharSequence implements CharSequence { final int start, size; MappedCharSequence(int start, int size) { this.start = start; this.size = size; } public int length() { return size; } public char charAt(int index) { if(index < 0 || index >= size) throw new IndexOutOfBoundsException(); byte b = mbb.get(start + index); return b<0? table[b+256]: (char)b; } public CharSequence subSequence(int start, int end) { int newSize = end - start; if(start<0 || end < start || end-start > size) throw new IndexOutOfBoundsException(); return new MappedCharSequence(start + this.start, newSize); } public String toString() { return new StringBuilder(size).append(this).toString(); } } return new MappedCharSequence(0, size); }}这允许将文件映射到虚拟内存并将其直接投影到
CharSequence,而无需复制操作,前提是可以使用一个简单的表进行映射,并且对于基于ASCII的字符集,大多数字符甚至都不需要一个表查找,因为它们的数值与Unipre代码点相同。
使用这些方法,您可以将操作实现为
// You need this only once per JVM.// Note that running inside IDEs like Netbeans may change the default encodingchar[] table = getTable(Charset.defaultCharset());try(Stream<Path> stream = Files.walk(Paths.get(FILES_DIRECTORY)) .filter(Files::isRegularFile)) { Pattern pattern = Pattern.compile(String.join("|", Strings_PASSWORDS)); long startTime = System.nanoTime(); final List<Path> filesWithObviousStringss = stream//.parallel() .filter(path -> { try { return pattern.matcher(mapAsciibasedText(path, table)).find(); } catch(IOException ex) { throw new UncheckedIOException(ex); } }) .collect(Collectors.toList()); System.out.println("Time taken = " + TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()-startTime) + " seconds");}它的运行速度比普通文本转换快得多,但仍支持并行执行。
除了需要基于ASCII的单字节编码外,还有一个限制是该代码不支持大于2
GiB的文件。尽管可以扩展解决方案以支持更大的文件,但除非确实需要,否则我不会添加这种复杂性。



