How do I remove the first 300 million lines from a 700 GB txt file on a system with 1 TB disk space?

Si cand auzi miliane de X, te gandesti la big data. Acum depinde si ce fel este X.

Este unul din exemplele de filozofie UNIX.

Sunt curios dacă nu ar merge mapat fişierul în RAM cu mmap(), numărate primele 300 de milioane de LF-uri pentru a sări de liniile care nu ne intereseaza, mutată coada spre faţă cu memcpy(), după care trunchiat fişierul sursa cu truncate().

În felul ăsta nu mai trebuie să generezi un fişier temporar şi să rişti să depăşeşti capacitatea de stocare. Plus că probabil e infinit mai performant decăt metoda compresiei.

Cam aşa ar arata codul, nu se consumă niciun byte în plus pe HDD şi nici nu alocăm explicit memorie în userspace, lăsam sistemul de operare să se descurce.

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>

#define SKIP_LINES 100

int main()
{
    int fd = open("maillog", O_RDWR);

    if(-1 == fd)
    {
        perror("open()");
        exit(1);
    }

    struct stat st;
    fstat(fd, &st);

    fprintf(stderr, "Initial file size size: %ld bytes\n", st.st_size);

    void *map = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE,  MAP_SHARED, fd, 0);

    if(MAP_FAILED == map)
    {
        perror("mmap()");
        close(fd);
        exit(1);
    }

    void *cursor = map;
    size_t size = st.st_size;

    // numaram LF-urile
    for(int count = 0; size > 0 && count < SKIP_LINES;  size = st.st_size - (cursor - map), count++)
    {
        cursor = memchr(cursor, '\n', size);
        if(NULL == cursor)
            break;

        cursor++;
    }

    fprintf(stderr, "Final file size size: %d\n", size);

    // mutam coada spre cap
    memcpy(map, cursor, size);

    munmap(map, st.st_size);

    ftruncate(fd, size);
    close(fd);

    return 0;
}

LE Nu-i aşa că nu v-aţi gândit niciodată să manipulati conţinutul unui fişier cu memcpy()? :slight_smile:

6 Likes