Дополнительно вы должны установить libpango-1.0-0
sudo apt install libpango-1.0-0
К сожалению, вы не можете обрезать / перезаписать строки в начале файла без перезаписи всего файла.
Я только что подумал о новом подходе, который может сделай за тебя трюк ...
Вы можете включить небольшой заголовок в свой файл, имеющий следующую структуру.
Изменить: Мусор, я только что описал вариант кольцевого буфера !
Поля заголовка
Байты 00–07 (длинные)
- Общее (текущее) количество строк, записанных в файл. Байты 08–15 (длинные)
- Указатель в начало «фактической» первой строки вашего файла. Первоначально это будет байт после окончания заголовка, но он изменится позже, когда данные будут переопределены. « Байты 16–23 (длинные)
- Длина« конечного раздела »файла. Очередной раз, изначально это будет ноль, но изменится позже, когда данные будут переопределены. Алгоритм чтения (псевдокод)
Считывает весь файл.
Read the header field that points to the start of the "actual" first line Read the header field that specifies the length of the "end section" Read every line until the end of the file Seek to the byte just after the end of the header Read every line until the "end section" has been fully read
Алгоритм записи (псевдокод)
Записывает произвольное количество новых строк в файл.
Read the header field that contains the total no. of lines in the file If (line count) + (no. of new lines) <= (maximum no. of lines) Then Append new lines to end of file Increment header field for line count by (no. of ne lines) Else Append as many lines as possible (up to maximum) to end of file Beginning at pointer to first line (in header field), read as many lines as still need to be written Find the total byte count of the lines just read Set the header field that points to the first line to the next byte in the stream Keep writing the new lines to the end of the file, each at a time, until the byte count of the remaining lines is less than the byte count of the lines at the beginning of the file (it may be that this condition is true immediately, in which case you don't need to write any more) Write the remaining new lines to the start of the file (starting at the byte after the header) Set the header field that contains the length of the "end section" of the file to the number of bytes just written after the header.
Я полностью признаю, что это не очень простой алгоритм! Тем не менее, я считаю, что это довольно элегантно. Дайте мне знать, если что-то из этого, конечно, непонятно. Надеюсь, он должен делать именно то, что вы хотите сейчас.
Теперь, если ваши строки гарантированно имеют постоянную длину (в байтах), вы можете достаточно легко просто вернуться к соответствующей точке и перезаписать существующие данные. Однако это может показаться весьма маловероятным. Если вы не против наложить ограничение, согласно которому ваши строки должны иметь максимальную длину, и дополнительно заполняя каждую из строк, которые вы пишете, до этой максимальной длины, тогда это может облегчить вам задачу. Тем не менее, у него есть свои недостатки, такие как значительное увеличение размера файла при определенных обстоятельствах (то есть большинство строк сильно укорочены, чем максимальная длина). Все зависит от ситуации, приемлемо это или нет ...
Наконец, вы можете вместо этого хотите рассмотреть возможность использования существующей системы регистрации, в зависимости от вашей конкретной цели.
Обычный способ ведения журнала, не увеличивающего размер, - использовать скользящие файлы журнала и прокручивать их один раз в день или подобное, и сохранять только N последних файлов.
Например, каждый день вы создаете новый файл журнала с именем `application_2009_05_20.log 'и начинаете писать в него , всегда добавляются.
Как только у вас будет 14-дневный журнал файлов, вы начинаете удалять самые старые.
Поскольку файлы ориентированы на байты, и вам нужна строчно-ориентированная служба, у вас есть два варианта:
реализовать строчно-ориентированную оболочку вокруг файла
переключиться на некоторую строку -ориентированное устройство. Я только что подумал: в SQLite есть несколько хороших оболочек C ++.
Простое решение:
Это решение разработано для обеспечения постоянной длины файла, а не постоянного количества строк в файле. Количество строк будет меняться со временем в зависимости от длины. Это решение затрудняет быстрый поиск конкретных номеров строк, хотя вы можете прикрепить некоторые индикаторные данные вверху или внизу файла, чтобы упростить это.
«Умное» решение (вариант решения, приведенного выше):
Просто используйте тот же трюк, который иногда используется для дек. Просто явно оберните от начала файла до конца, но следите за тем, где находится начало / конец файла. Вы можете написать служебную программу для преобразования этого файла в стандартный, если хотите прочитать его программой, которая его не поддерживает. Это решение ДЕЙСТВИТЕЛЬНО легко реализовать, но мне больше нравится версия выше.
Уродливое решение:
Добавляя строки, добавьте умеренное количество отступов к каждой добавляемой строке.
Каждый раз, когда вы хотите добавить новая строка, выполните следующие действия:
Обратите внимание, что это будет работать очень плохо, если ваши строки не будут достаточно последовательными по длине. Более простое решение - гарантировать, что строки имеют постоянную длину (но таким образом можно создать многострочные «линии» на случай, если вы превысите эту длину.
Используйте кольцевой буфер и записывайте буфер в файл для каждого добавления.
Вот небольшое и простое решение для размера кода. Это простой кольцевой буфер строк, и каждый раз, когда вы добавляете строки, он записывает весь буфер строк в файл (конечно, вы несете значительные затраты на запись всех строк для одна операция добавления. Так что это подходит только для небольшого количества строк).
Простая реализация кольцевого буфера с выводом в файл:
// GLOBALS ( final implementation should not use globals )
#define MAX_CHARS_PER_LINE (1024)
#define MAX_ITEMS_IN_CIRCULARBUF (4) // must be power of two
char lineCircBuf[MAX_ITEMS_IN_CIRCULARBUF][MAX_CHARS_PER_LINE];
int lineCircBuf_add = 0;
int lineCircBuf_rmv = 0; // not being used right now
uint32_t lineCircBuf_mask = MAX_ITEMS_IN_CIRCULARBUF-1;
char FILENAME[] = "lineCircBuf.txt";
FILE * ofp = NULL;
int addLine(char * str) {
int i;
// Error checking
if( strlen(str) > MAX_CHARS_PER_LINE ) {
return -1; // failure
}
if( ofp != NULL) {
fclose(ofp);
}
// Copy string into circular buffer
strncpy( &(lineCircBuf[lineCircBuf_add][0]),
str,
MAX_CHARS_PER_LINE );
lineCircBuf_add = ( lineCircBuf_add + 1 ) & lineCircBuf_mask;
// Write to file
ofp = fopen(FILENAME,"w");
for( i = 0; i < MAX_ITEMS_IN_CIRCULARBUF-1; i++ ) {
fprintf( ofp, "%s\n", lineCircBuf[i] );
}
fprintf( ofp, "%s", lineCircBuf[i] ); // do not add a newline to the last line b/c we only want N lines in the file
return 0; // success
}
int removeLine(int index) {
// not implemented yet
}
void unitTest() {
int i;
// Dummy text to demonstrate adding string lines
char lines[5][MAX_CHARS_PER_LINE] = {
"Hello world.",
"Hello world AGAIN.",
"The world is interesting so far!",
"The world is not interesting anymore...",
"Goodbye world."
};
// Add lines to circular buffer
for( i = 0; i < sizeof(lines)/sizeof(lines[0]); i++ ) {
addLine(&(lines[i][0]));
}
}
int main() {
unitTest();
return 0;
}
Итак, в приведенном выше примере у нас было 5 строк ввода, а длина нашего буфера составляла всего 4 строки. Таким образом, вывод должен состоять только из 4 строк, и первая строка должна быть заменена последней строкой «Goodbye world». Конечно, первая строка вывода подтверждает, что действительно есть "Goodbye world":
Goodbye world.
Hello world AGAIN.
The world is interesting so far!
The world is not interesting anymore...
Просто создайте отображение файла требуемого размера (CreateFileMapping или mmap), запишите строки в буфер и начните заново, когда достигнуто максимальное количество.
, если файлы должны быть текстовыми:
Это очень проблематично при различной длине строк. Ваши первые две строки состоят из 80 символов каждая, как вы замените это строкой из 100 символов?
Если новая строка должна заменить первую строку , это приведет к вставке файла, который является очень дорогостоящая операция (как правило, необходимо читать и записывать весь оставшийся файл). Вы действительно не хотите делать это для всех, кроме минимальных объемов данных.
Если это необходимо для записи в журнал, используйте файлы журналов, например, один раз в день (как предлагает lassevek). Я сделал это еще проще: когда размер файла превышает предел, старый файл переименовывается в .bak (старый .bak удаляется) и запускается заново. При ограничении в 1 МБ это сохраняет, например, последний 1 МБ, но никогда не занимает больше 2 МБ.
Вы можете использовать аналогичный механизм с двумя или более файлами. По сути, перемещайте «ролловер» на файлы, а не на строки.
если файл может быть в частном формате:
Используйте базовый механизм БД (например, SQLite, как предлагается) или другой механизм структурированного хранения.
Вы можете использовать log4cxx
с RollingFileAppender
для записи этой информации в файл журнала. RollingFileAppender
будет обрабатывать пролистывание файла журнала, когда он достигнет определенного размера. Я не думаю, что это именно то, что вы хотите, но это довольно просто - может быть, подойдет.
Простое решение:
, это будет сложно, поскольку файловый ввод-вывод работает с байтами в качестве базовой единицы хранения, а не со строками.
Я имею в виду, что вы можете просто вернуться к началу с помощью fseek () и уничтожьте более ранние данные, но я подозреваю, что это не то, что вам нужно.
Если вы хотите сгенерируйте этот файл для ввода в другое приложение, я думаю, вам лучше всего будет регистрироваться непосредственно в базе данных отношений (SQL Server, MySQL, что угодно ...) Затем периодически генерировать этот файл по мере необходимости из зарегистрированных данных.
Чтобы обойти проблему с переменным размером, вы, вероятно, получите косвенную схему и схему распределения. Он будет состоять из блока косвенного обращения с фиксированным количеством «указателей» в файл и одного указателя «следующего за записью», который будет обертываться вокруг N.
Но главный трюк будет заключаться в добавлении косвенное обращение.
Я видел, как это делалось, сохраняя где-нибудь текущую позицию записи для файла. Когда вам нужно добавить строку, вы переходите к позиции, записываете строку и обновляете позицию атомарным способом. Если вы переполняетесь, вы стремитесь к нулю, прежде чем писать строку. Мы делаем это сегодня для файлов журналов с ограниченным размером. Выполнение этого на основе ограниченного количества строк немного странно, но, вероятно, можно было бы сделать аналогичным образом. Наш цикл записи выглядит примерно так:
logFile.lockForWrite();
currentPosition = logFile.getWritePosition();
logFile.seek(currentPosition);
for each line in lineBuffer {
if ((currentPosition+line.length()) > logFile.getMaxSize()) {
currentPosition = 0;
logFile.seek(0);
}
logFile.write(line);
currentPosition += line.length();
}
logFile.setWritePosition(currentPosition);
logFile.unlock();
Сложная часть состоит в том, чтобы поддерживать текущую позицию записи и находить способ координировать чтение файла (например, с помощью утилиты tail
), пока ваше приложение пишет в него. Утилита чтения должна также отслеживать позицию записи, поэтому цикл чтения выглядит следующим образом:
lastPosition = logFile.getWritePosition();
while (!killed) {
logFile.wait();
logFile.lockForRead();
newPosition = logFile.getWritePosition();
logFile.seek(lastPosition);
newLine = logFile.readFrom(lastPosition, (newPosition-lastPosition));
lastPosition = newPosition;
logFile.unlock();
}
This isn ' t на каком-то конкретном языке - это просто псевдокод, но идея есть. Конечно, я оставил рассмотрение всех интересных крайних случаев читателю.
С учетом всего сказанного ... Я согласен с другими мнениями. Не делайте этого, если у вас нет действительно веской причины. Это звучит как отличная идея, но:
grep
, tail
, perl
и т. д. В целом, вам будет лучше использовать какой-нибудь существующий пакет регистрации пакетов, который позволяет настраивать управление файлами журнала. Взгляните на Apache log4cxx или Poco's Poco :: Logger
.