моя реализация похожа, хотя и не такая аккуратная:
class MyClass:
def __init__(self, name):
self.name = name
def __add__(self, other):
for x in range(1, len(self.name)):
a = list(self.name)[-1 * x]
return self.name[:len(self.name)-x] + chr(ord(a) + other)
>>> CAA = MyClass('CAA')
>>> CAA + 1
'CAB'
>>> CAA + 2
'CAC'
>>> CAA + 15
'CAP'
Я большой большой поклонник Java MappedByteBuffers
для подобных ситуаций. Это пылает быстро. Ниже приведен фрагмент, который я собрал для вас, который отображает буфер в файл, ищет в середине, а затем ищет символ новой строки в обратном направлении. Этого должно быть достаточно, чтобы начать работу?
У меня есть подобный код (поиск, чтение, повтор до завершения) в моем собственном приложении, сравнительный тест
java.io
направляет поток MappedByteBuffer
в производственной среде и публикует результаты в моем блоге ( посты с геоматикой, помеченные 'java.nio' ) с необработанными данными, графиками и все.
Два вторых резюме? Моя реализация на основе MappedByteBuffer
была примерно на 275% быстрее. YMMV.
Для работы с файлами размером более ~ 2 ГБ, что является проблемой из-за приведения и .position (int pos)
, я создал алгоритм подкачки, поддерживаемый массивом MappedByteBuffer
S. Вам нужно будет работать в 64-битной системе, чтобы она работала с файлами размером более 2-4 ГБ, потому что МББ используют систему виртуальной памяти ОС, чтобы творить свое волшебство.
public class StusMagicLargeFileReader {
private static final long PAGE_SIZE = Integer.MAX_VALUE;
private List<MappedByteBuffer> buffers = new ArrayList<MappedByteBuffer>();
private final byte raw[] = new byte[1];
public static void main(String[] args) throws IOException {
File file = new File("/Users/stu/test.txt");
FileChannel fc = (new FileInputStream(file)).getChannel();
StusMagicLargeFileReader buffer = new StusMagicLargeFileReader(fc);
long position = file.length() / 2;
String candidate = buffer.getString(position--);
while (position >=0 && !candidate.equals('\n'))
candidate = buffer.getString(position--);
//have newline position or start of file...do other stuff
}
StusMagicLargeFileReader(FileChannel channel) throws IOException {
long start = 0, length = 0;
for (long index = 0; start + length < channel.size(); index++) {
if ((channel.size() / PAGE_SIZE) == index)
length = (channel.size() - index * PAGE_SIZE) ;
else
length = PAGE_SIZE;
start = index * PAGE_SIZE;
buffers.add(index, channel.map(READ_ONLY, start, length));
}
}
public String getString(long bytePosition) {
int page = (int) (bytePosition / PAGE_SIZE);
int index = (int) (bytePosition % PAGE_SIZE);
raw[0] = buffers.get(page).get(index);
return new String(raw);
}
}
Это простой пример того, чего вы хотите достичь. Я бы, вероятно, сначала проиндексировал файл, отслеживая положение файла для каждой строки. Я предполагаю, что строки разделены символами новой строки (или возврата каретки):
RandomAccessFile file = new RandomAccessFile("filename.txt", "r");
List<Long> indexList = new ArrayList();
long pos = 0;
while (file.readLine() != null)
{
Long linePos = new Long(pos);
indexList.add(linePos);
pos = file.getFilePointer();
}
int indexSize = indexList.size();
Long[] indexArray = new Long[indexSize];
indexList.toArray(indexArray);
Последний шаг - преобразование в массив для небольшого улучшения скорости при выполнении большого количества поисков. Я бы, вероятно, также преобразовал Long []
в long []
, но я не показал этого выше. Наконец, код для чтения строки из заданной индексированной позиции:
int i; // Initialize this appropriately for your algorithm.
file.seek(indexArray[i]);
String line = file.readLine();
// At this point, line contains the string #i.
Если вы действительно хотите попробовать отображение памяти файл,
Я не знаю ни одной библиотеки, обладающей такой функциональностью. Однако правильный код для внешнего бинарного поиска в Java должен быть похож на это:
class ExternalBinarySearch {
final RandomAccessFile file;
final Comparator<String> test; // tests the element given as search parameter with the line. Insert a PrefixComparator here
public ExternalBinarySearch(File f, Comparator<String> test) throws FileNotFoundException {
this.file = new RandomAccessFile(f, "r");
this.test = test;
}
public String search(String element) throws IOException {
long l = file.length();
return search(element, -1, l-1);
}
/**
* Searches the given element in the range [low,high]. The low value of -1 is a special case to denote the beginning of a file.
* In contrast to every other line, a line at the beginning of a file doesn't need a \n directly before the line
*/
private String search(String element, long low, long high) throws IOException {
if(high - low < 1024) {
// search directly
long p = low;
while(p < high) {
String line = nextLine(p);
int r = test.compare(line,element);
if(r > 0) {
return null;
} else if (r < 0) {
p += line.length();
} else {
return line;
}
}
return null;
} else {
long m = low + ((high - low) / 2);
String line = nextLine(m);
int r = test.compare(line, element);
if(r > 0) {
return search(element, low, m);
} else if (r < 0) {
return search(element, m, high);
} else {
return line;
}
}
}
private String nextLine(long low) throws IOException {
if(low == -1) { // Beginning of file
file.seek(0);
} else {
file.seek(low);
}
int bufferLength = 65 * 1024;
byte[] buffer = new byte[bufferLength];
int r = file.read(buffer);
int lineBeginIndex = -1;
// search beginning of line
if(low == -1) { //beginning of file
lineBeginIndex = 0;
} else {
//normal mode
for(int i = 0; i < 1024; i++) {
if(buffer[i] == '\n') {
lineBeginIndex = i + 1;
break;
}
}
}
if(lineBeginIndex == -1) {
// no line begins within next 1024 bytes
return null;
}
int start = lineBeginIndex;
for(int i = start; i < r; i++) {
if(buffer[i] == '\n') {
// Found end of line
return new String(buffer, lineBeginIndex, i - lineBeginIndex + 1);
return line.toString();
}
}
throw new IllegalArgumentException("Line to long");
}
}
Обратите внимание: я составил этот код ad-hoc: угловые случаи не достаточно хорошо протестированы, код предполагает, что ни одна строка не является больше чем 64K и т. д.
Я также думаю, что хорошей идеей может быть создание индекса смещений, с которых начинаются линии. Для файла объемом 500 ГБ этот индекс должен храниться в файле индекса. Вы должны получить не такой маленький постоянный коэффициент с этим индексом, потому что нет необходимости искать следующую строку на каждом шаге.
Я знаю, что это был не вопрос, но построение структуры данных префиксного дерева, такой как (Patrica) Tries (на диске / SSD), может быть хорошей идеей для поиска префикса.
У меня такая же проблема. Я пытаюсь найти в отсортированном файле все строки, начинающиеся с некоторого префикса.
Вот метод, который я приготовил, который в значительной степени является портом кода Python, найденного здесь: http://www.logarithmic.net/pfh/blog/01186620415
Я тестировал его, но не полностью, просто все же. Однако он не использует отображение памяти.
public static List<String> binarySearch(String filename, String string) {
List<String> result = new ArrayList<String>();
try {
File file = new File(filename);
RandomAccessFile raf = new RandomAccessFile(file, "r");
long low = 0;
long high = file.length();
long p = -1;
while (low < high) {
long mid = (low + high) / 2;
p = mid;
while (p >= 0) {
raf.seek(p);
char c = (char) raf.readByte();
//System.out.println(p + "\t" + c);
if (c == '\n')
break;
p--;
}
if (p < 0)
raf.seek(0);
String line = raf.readLine();
//System.out.println("-- " + mid + " " + line);
if (line.compareTo(string) < 0)
low = mid + 1;
else
high = mid;
}
p = low;
while (p >= 0) {
raf.seek(p);
if (((char) raf.readByte()) == '\n')
break;
p--;
}
if (p < 0)
raf.seek(0);
while (true) {
String line = raf.readLine();
if (line == null || !line.startsWith(string))
break;
result.add(line);
}
raf.close();
} catch (IOException e) {
System.out.println("IOException:");
e.printStackTrace();
}
return result;
}
Если вы имеете дело с файлом размером 500 ГБ, вы можете использовать более быстрый метод поиска, чем двоичный поиск, а именно сортировку по основанию, которая, по сути, является вариантом хеширования. Лучший способ сделать это действительно зависит от вашего распределения данных и типов поиска, но если вы ищете строковые префиксы, должен быть хороший способ сделать это.
Я опубликовал пример решения сортировки по основанию для целых чисел, но вы можете использовать ту же идею - в основном, чтобы сократить время сортировки, разделив данные на сегменты, а затем используя поиск O (1) для извлечения группы данных, которые имеют значение.
Option Strict On
Option Explicit On
Module Module1
Private Const MAX_SIZE As Integer = 100000
Private m_input(MAX_SIZE) As Integer
Private m_table(MAX_SIZE) As List(Of Integer)
Private m_randomGen As New Random()
Private m_operations As Integer = 0
Private Sub generateData()
' fill with random numbers between 0 and MAX_SIZE - 1
For i = 0 To MAX_SIZE - 1
m_input(i) = m_randomGen.Next(0, MAX_SIZE - 1)
Next
End Sub
Private Sub sortData()
For i As Integer = 0 To MAX_SIZE - 1
Dim x = m_input(i)
If m_table(x) Is Nothing Then
m_table(x) = New List(Of Integer)
End If
m_table(x).Add(x)
' clearly this is simply going to be MAX_SIZE -1
m_operations = m_operations + 1
Next
End Sub
Private Sub printData(ByVal start As Integer, ByVal finish As Integer)
If start < 0 Or start > MAX_SIZE - 1 Then
Throw New Exception("printData - start out of range")
End If
If finish < 0 Or finish > MAX_SIZE - 1 Then
Throw New Exception("printData - finish out of range")
End If
For i As Integer = start To finish
If m_table(i) IsNot Nothing Then
For Each x In m_table(i)
Console.WriteLine(x)
Next
End If
Next
End Sub
' run the entire sort, but just print out the first 100 for verification purposes
Private Sub test()
m_operations = 0
generateData()
Console.WriteLine("Time started = " & Now.ToString())
sortData()
Console.WriteLine("Time finished = " & Now.ToString & " Number of operations = " & m_operations.ToString())
' print out a random 100 segment from the sorted array
Dim start As Integer = m_randomGen.Next(0, MAX_SIZE - 101)
printData(start, start + 100)
End Sub
Sub Main()
test()
Console.ReadLine()
End Sub
End Module