PDA

Prikaži potpunu verziju : File splitter


Futuristic
12.12.2012, 19:16
Skoro sam prelistavao temu Programming Challenges i video da je neko postavio zadatak sa file splitterom. Delovalo mi je interesantno pa sam i ja pokusao.
Prvo sam krenuo da naucim malo file I/O pa sam onda pokusao prvo da napravim da mi se fajl deli na konstantan broj, npr. 4 i jos jedan fajl u slucaju da velicina fajla nije deljiva sa 4.
Potom sam smestio ceo fajl u memoriju i sad treba da ucitam u ova 4 odnosno 5 fajlova.
Problem je kad ucitam deo prvobitnog fajla u prvi i krenem na drugi, hocu da odbacim iz buffera ono sto sam ucitao u prvi, i da nastavim ucitavanje u drugi itd.
Sad ja ne znam kako to da uradim, moze pomoc ?

Geomaster
14.12.2012, 19:14
Možeš li malo da pojasniš, nije mi jasno šta tačno želiš?

MG-RAY
14.12.2012, 20:51
Želi file splitter. :D

@Futuristic
Na dobrom si putu, mada bi bilo bolje da ne ucitavas ceo fajl u memoriju posto oni mogu biti ogromni, vec deo po deo, recimo, u chunkovima od po 4kb, i da njih upisujes u izlazne fajlove.

Koji jezik je u pitanju btw?

ivan90BG
15.12.2012, 3:00
Ovo je kod samo za slučaj kada je fajl veći od veličine jednog komada i kad je bafer manji od veličine komada, jer je to najkompikovaniji, ostali slučajevi su lakši, to sam napiši. Pisao sam iz glave, pa možda ima grešaka. Ako ne radiš u C#-u, ipak možeš da shvatiš algoritam i da ga prevedeš u tvoj jezik.


byte[] buffer = new byte[4096];
File file = File.OpenRead("ime fajla");
file.Read(buffer);
int velicinaKomada;
int ostalo = velicinaKomada; //koliko je ostalo da se upiše u trenutni fajl
int uBaferu = buffer.Length; //koliko je ostalo u trenutnom baferu
int pos = 0; //koliko je upisano iz trenutnog bafera
int fajlova = file.Size / velicinaKomada;
int ostatak = file.Size % velicinaKomana;
int i = 1;
File trenutni = File.OpenWrite(file.Name + (i++));
while (true)
{
if (uBaferu > ostalo)
{
trenutni.Write(buffer,pos,ostalo);
pos += ostalo;
uBaferu -= ostalo;
trenutni.Close();
i++;
if (i < fajlova + 1)
break;
trenutni = File.OpenWrite(file.Name + i);
ostalo = i == brojFajlova+1 ? velicinaKomada : ostatak;
}
else
{
trenutni.Write(buffer,pos,uBaferu);
ostalo -= uBaferu;
file.Read(buffer);
uBaferu = buffer.Length;
pos = 0;
}
}
file.Close();

Futuristic
15.12.2012, 11:04
Hvala na svim odgovorima.
Evo, u pocetku sam imao problem da napravim da mi deli na 4 dela, nekako sam uspeo da napravim prog. gde korisnik moze da unese zeljenu kolicinu fajlova.
U krajnju ruku, program radi, recite mi sta ne valja, sta bi se moglo unaprediti (error handling pri otvaranju, upisu fajlova itd, nisam se bas tome posvetio).
#include <stdio.h>
#include <stdlib.h>

int main()
{
FILE *src,*partx = NULL;
char *buffer;
FILE **files;
int size,partSize,xSize,noOfFiles,i,j=0;
char fileName[50];


printf("File to be split: ");
scanf("%s",fileName);

if((src = fopen(fileName,"rb")) == NULL)
{
printf("Error opening file,exiting....");
exit(EXIT_FAILURE);
}

fseek(src, 0, SEEK_END);
size = ftell(src);
fseek(src, 0, SEEK_SET);
printf("File size is %d bytes\n",size);
printf("Enter the number of parts you want to split the original to: ");
scanf("%d",&noOfFiles);
partSize = size / noOfFiles;
xSize = size % noOfFiles;
printf("Size of each of %d parts is %d bytes\n",noOfFiles,partSize);
if(xSize)
printf("Size of an additional file is %d bytes\n",xSize);



files = (FILE**) malloc(noOfFiles*sizeof(char *));

for(i=0;i<noOfFiles;i++)
{
char c[10];
itoa(i,c,10);
if((files[i] = fopen(c,"wb")) == NULL)
{
printf("Error creating file %d, exiting...",i);
exit(EXIT_FAILURE);
}
}
if(xSize)
partx = fopen("x","wb");

buffer = (char*) malloc (sizeof(char)*size);
if(buffer == NULL)
{
printf("Failed allocating enough memory");
exit(EXIT_FAILURE);
}
for(i=0;i<noOfFiles;i++)
{
fseek(src,i*partSize,SEEK_SET);
fread(buffer,1,partSize,src);
fwrite(buffer,1,partSize,files[i]);
printf("Writing to file %d...\n",i);
j++;
}

if(partx!= NULL)
{
fseek(src,j*partSize,SEEK_SET);
fread(buffer,1,xSize,src);
fwrite(buffer,1,xSize,partx);
printf("Writing to the additional file...\n");
}
fclose(src);
for(i=0;i<noOfFiles;i++)
fclose(files[i]);

if(partx!= NULL)
fclose(partx);
free(buffer);
return 0;

voodoo_
15.12.2012, 11:08
Pravio sam nešto na ovu temu pre pet godina u Delphiju

http://dl.dropbox.com/u/22166389/vfc-1.01.zip

http://i47.tinypic.com/33xzr7p.png

Glavni kod je u frm_main.pas, relativno lako može da se portuje u .NET

ivan90BG
16.12.2012, 12:32
@Futuristic

Ovakav program kako si napisao će imati problema sa resursima. Šta ako neko hoće da podeli fajl od 3 GB na delove po 100 MB? Onda će program tokom rada držati 30 otvorenih fajlova i zauzimati 100 MB memorije za bafer.

MG-RAY
16.12.2012, 13:57
Možeš da ubaciš i neki checksum za splitove i za pocetni fajl kako bi znao da se sve lepo spojilo, npr, SHA (http://stackoverflow.com/questions/1993903/how-do-i-do-a-sha1-file-checksum-in-c) ili slične.

I "pozajmi" poneku ideju iz Ivanovog koda jer radi ono što treba. :D

Takođe, kada budeš birao veličinu chunkova za kopiranje, izaberi neki koji je deljiv sa 512 (što je veličina sektora), ali najbolje zadrži 4K (4096), jer su clusteri na diskovima uglavnom te veličine danas. Plus, sa tako malim paketima iskoristićeš i potencijale keširanja tako da imaš poprilično optimalno rešenje.

ivan90BG
16.12.2012, 15:18
Takođe je 4 kB veličina jedne memorijske stranice.
Ali kad bolje razmislim, čini mi se da zavisi od implementacije da li će za alokaciju 4 KB biti odvojena tačno jedna stranica memorije, jer i alokator mora da "vodi knjige" i za to koristi memoriju, pa je moguće da alokator alokaciju od 4K protera kroz uobičaenu proceduru (u kojoj može da se desi da za ovu alokaciju "potroši" dve stranice, zbog dodatnih knjigovodstvenih podataka), a možda baš prepozna da je tražena veličina umnožak veličine stranice, pa zatraži od OS-a jednu novu stranicu, a svoje knjigovodstvene podatke sačuva negde u slobodan prostor koji već ima.

Geomaster
16.12.2012, 15:27
Linuxov malloc, koliko se ja sećam, pruža takvu optimizaciju po veličini memorijske stranice, za Windows ne znam ali trebalo bi i on. U svakom slučaju, ako hoćeš da budeš siguran, koristi neki portabilni, optimizovan alokator (prvi mi pada na pamet nedmalloc).

Futuristic
26.12.2012, 17:20
Evo posle malo duze vremena uspeo sam da sklepam splitter, na vas predlog sa bufferom od 4 kB i onda sam se petljao sa onim koliko je upisano, koliko jos ostaje itd...
Sad ostaje napraviti merger pa mi recite kako je to najbolje uraditi? Da korisnik unosi fajl po fajl mi se cini cudno i vrvt ima bolje resenje.

@Ivan90BG Cim si to rekao, ubacio sam kao input file od 4 GB i izasla je greska :D

@MG-RAY Citao sam o CRC, ali ne znam kako bih napisao kod za to.
Ide nesto na foru, imas polinom generator, na osnovu njegovog stepena prosiris podatak koji crc-ujes, onda uradis xor podatka i polinoma u bin. zapisu , radis to dok ne dobijes ostatak, i taj ostatak nakacis na orig. podatak. Posle kad bi uradio isto to na kodiran podatak trebalo bi da dobijes ost. 0 sto znaci da su identicni. Moguce da sam izlupetao, pa nek me neko ispravi i razjasni nedoumice.

Btw. kada u splitteru prijavljujem velicinu fajla, za manje fajlove dobijem korektan podatak dok za jedan od 4GB nisam dobio tacan.
Podatak o velicini sam poredio sa onim sto se dobije kad se ode na fajl, desni-klik, properties. Taj fajl od 4 GB je bio .iso ako nesto znaci. Koristio sam unsigned long int da smestim podatak, ne verujem da je preslo opseg, iako ej bilo blizu.

MG-RAY
27.12.2012, 0:39
Možeš da implementiras CRC checksum ili da koristiš već implementirani SHA1 / SHA256 iz frameworka.

Za sam merge, možeš da upišeš podatke o splitovima u poseban fajl i tu držiš i hasheve, ili na početku prvog fajla pa bi njega prosledio mergeru?