Wewnętrzna edycja pliku

Hej, możecie pomóż?

Jak moge posortować linijki w pliku według ich długości, czyli od najkrótszych po najdłuższe?

A potem każdą długość linijek jeszcze po sortować alfabetycznie?

 

Trzeba by jakiś skrypt napisać. Musiałbyś się zastanowić w czym by Ci było najłatwiej - czy w bashu, czy może w Javie albo pytonie.

Trzeba zczytać poszczególne linijki do tablicy, odczytać ich długość, zastosować sortowanie po długości, a później posortować alfabetycznie. W Javie byłoby tyle łatwo, że masz gotowe biblioteki sortowania, ale w bashu tez by się pewnie dało.

Tylko że, plik waży około 6Gb i pojawia się pytanie czy skrypt by to uciągnął :slight_smile: ?

wc -l ZapisRoczny.txt

 wynik to 251954075 linijek (po potraktowaniu uniq i sort);

To faktycznie zmienia postać rzeczy. Można by spróbować wykorzystać Excela, ale tez nie jestem pewien jakby sobie poradził z importem tak dużego pliku. Teoretycznie wersja 64-bitowa powinna sobie poradzić. Wtedy można wykorzystać mechanizmy Excela i ewentualnie skrypt VBA. 

Ewentualnie można by poszukać czy jest jakaś biblioteka, która potrafiła by to wyeksportować do bazy danych i wtedy można w bazie manipulować, tylko trzeba by poczytać w jaki sposób odczytywać tak wielki plik. Być może jakby czytać strumieniowo bajt po bacie to wtedy dałoby radę. Takie luźne pomysły rzucam.

Lepiej zatrudnić do tego Perl lub Ruby.

 

Coś w tym stylu:

http://stackoverflow.com/questions/12417403/how-to-sort-the-lines-in-a-file-from-shortest-to-longest/12427379#12427379

Jak mam to zaimpletować aby był potok :)?

kilkanaście GB mnie nie zbawi:

raportroczny.txt >> awk? >> gotowyraport.txt

Chyba chodziło o

cat raportroczny.txt | awk | gotowyraport.txt

Niech program zczyta do tablicy tylko długości stringów. Następnie niech posortuje według tego klucza, a dopiero na końcu wstawi odpowiednie stringi do nowego pliku.

 

Moja implementacja:

https://gist.github.com/enedil/60a9315128e43813f123

 

Nie wiem jak wydajnie będzie działać dla takich dużych plików.

Pobrałem sobie ten twój kod z https://gist.github.com/enedil/60a9315128e43813f123/download# potem, się ROOTuje

i próbuje to skompilować g++ -g -Wall -o sortByLineLength sortByLineLength.cpp a wynik tego taki:

root@etc:/home/etc/Pobrane# g++ -g -Wall -o sortByLineLength sortByLineLength.cpp
sortByLineLength.cpp:37:47: error: ‘>>’ should be ‘> >’ within a nested template argument list
void readFile(std::vector<std::pair<int, short>>&, const char*);
^
sortByLineLength.cpp:38:54: error: ‘>>’ should be ‘> >’ within a nested template argument list
void writeFile(const std::vector<std::pair<int, short>>&, const char*, const char*);
^
sortByLineLength.cpp:40:52: error: ‘>>’ should be ‘> >’ within a nested template argument list
int findPair(const std::vector<std::pair<int, short>>&, int);
^
sortByLineLength.cpp: In function ‘int main(int, char**)’:
sortByLineLength.cpp:45:34: error: ‘>>’ should be ‘> >’ within a nested template argument list
std::vector<std::pair<int, short>> v;
^
sortByLineLength.cpp: At global scope:
sortByLineLength.cpp:53:47: error: ‘>>’ should be ‘> >’ within a nested template argument list
void readFile(std::vector<std::pair<int, short>>& v, const char* path)
^
sortByLineLength.cpp:64:54: error: ‘>>’ should be ‘> >’ within a nested template argument list
void writeFile(const std::vector<std::pair<int, short>>& v, const char* inPath, const char* outPath)
^
sortByLineLength.cpp:79:52: error: ‘>>’ should be ‘> >’ within a nested template argument list
int findPair(const std::vector<std::pair<int, short>>& v, int key)
^
sortByLineLength.cpp: In function ‘std::string readNthLine(const char*, int)’:
sortByLineLength.cpp:96:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
for(size_t i = 0; i < n; ++i) {
^
root@etc:/home/etc/Pobrane#

7 błedów i 1 ostrzeżenie :slight_smile: pluje się o te twoje >>  i o zmienną int n, poprawiłem twój kod:

/*
The MIT License (MIT)

Copyright © 2015 enedil

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

// compile this source file and run:
// ./binaryFileName inputFile outputFile

#include
#include
#include
#include
#include // std::pair
#include

// function prototypes:
void readFile(std::vector<std::pair<int, short> >&, const char*);
void writeFile(const std::vector<std::pair<int, short> >&, const char*, const char*);
bool comparePairs(const std::pair<int, short>&, const std::pair<int, short>&);
int findPair(const std::vector<std::pair<int, short> >&, int);
std::string readNthLine(const char*, int);

int main(int argc, char* argv[])
{
std::vector<std::pair<int, short> > v;
const char* inputFile = argv[1];
const char* outputFile = argv[2];
readFile(v, inputFile);
std::sort(v.begin(), v.end(), comparePairs);
writeFile(v, inputFile, outputFile);
}

void readFile(std::vector<std::pair<int, short> >& v, const char* path)
{
std::ifstream file(path);
std::string line;
size_t i = 0;
while (std::getline(file, line)) {
v.push_back(std::make_pair(line.size(), i));
++i;
}
}

void writeFile(const std::vector<std::pair<int, short> >& v, const char* inPath, const char* outPath)
{
std::ofstream outFile(outPath);

for (size_t i = 0; i < v.size(); ++i) {
outFile << readNthLine(inPath, findPair(v, i)) << std::string("\n");
}

}

bool comparePairs(const std::pair<int, short>& p1, const std::pair<int, short>& p2)
{
return p1.first < p2.first;
}

int findPair(const std::vector<std::pair<int, short> >& v, int key)
{
for (size_t i = 0; i < v.size(); ++i) {
if (v[i].second == key) {
return i;
}
}
return -1;
}

std::string readNthLine(const char* filename, int n)
{
std::ifstream in(filename);

std::string s;

//skip n lines
for(size_t i = 0; i < n; ++i) {
std::getline(in, s);
}

std::getline(in, s);
return s;
}

I teraz tylko wywala to “int n”, nie moge znaleźć tego błędu, pewnie coś przeoczam:

root@etc:/home/etc/Pobrane# g++ -g -Wall -o sortByLineLength sortByLineLength.cpp
sortByLineLength.cpp: In function ‘std::string readNthLine(const char*, int)’:
sortByLineLength.cpp:96:22: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
for(size_t i = 0; i < n; ++i) {
^
root@etc:/home/etc/Pobrane#

Ale się kompiluje :slight_smile:

Uruchamiam:

root@etc:/home/etc/Pobrane# ./sortByLineLength test.txt out.txt

Jakiś plik przed posortowaniem:

//test.txt (bez tej linijki)
ffdsffff
sdfggfadgfdgdfgdfg
fgd

fgggfgfgfg
fgfgggggg13fdnbyewg78hfu43hf7hu3h73hf
nufh3uhfunfurnbui
nfnu3nu
3f
fgfgfggffgf
ffffffffffffffffgf
fgfgfgfgfgfg
gdgfgfgfg

a po sortowaniu:

fgggfgfgfg
ffffffffffffffffgf
fgd
ffdsffff
nufh3uhfunfurnbui
gdgfgfgfg
fgfgfggffgf

sdfggfadgfdgdfgdfg
nfnu3nu
fgfgfgfgfgfg
3f
fgfgggggg13fdnbyewg78hfu43hf7hu3h73hf

Ale efekt jest znikomy? Bo “fgd” jest krótsze niż “fgggfgfgfg” a mimo to jest niżej :slight_smile:

To nie sortuje? Od najkrótszej linijki do najdłuższej? Nie musi być alfabetycznie jak

napisałem w pierwszym poście, WYSTARCZY tylko według długości.

Ostrzeżenie “comparison between signed and unsigned integer expressions” można zignorować, gdyż mówi tylko tyle, że błędy czasu uruchamiania mogłyby wystąpić, gdyby n było ujemne.

 

Niestety, coś sknociłem.

Dla danych, które sprawdzałem działał poprawnie. Nie mam motywacji, by szukać rozwiązania.

 

BTW.

cat test
a
fffff
aaaaaaaa
ccc
bb
dddd

perl -e 'print sort {length $a <=> length $b} <>' test > test2

cat test2
a
bb
ccc
dddd
fffff
aaaaaaaa

Nie wiem czy obrobi tak wielki plik w całości.

Napisałem na szybko takie sortowanie w Javie (tylko po długości). Mogę Ci podesłać gotowego jar’a, albo możesz sobie sam

skompilować kod. Testowałem na danych, które zamieściłeś powyżej. Żeby było prościej wpisz sobie na sztywno nazwę pliku w kodzie. U mnie plik źródłowy nazywa się zdrodlowy.txt, a posortowany nazywa sie posortowany.txt. Po posortowaniu tworzonny jest nowy plik. Plik źródłowy powinien się znajdować w folderze, z którego jest uruchamiany ten kod.

 

Nie mam za bardzo takich dużych plików tekstowych o zmiennej długości linii. Trzeba by jakiegoś wygenerować i na nim po testować wydajność. Może coś wymyślę i stworzę coś wielkości kilku MB. Dam znać, jeśli coś z tego wyjdzie.

 

EDIT:

 

Wygenerowałem sobie pliczek składający się z miliona linii o losowej długości do maksymalnie 120 znaków w linii, który zajął 59,5 MB. Sortowanie zajęło około 3 sekund na moim kompie. Teoretycznie więc Twój plik powinien być posortowany w ciągu kilku minut. 

Aha, zapomniałem dodać, że lepiej jest odpalić skompilowany kod z konsoli. Wydaje mi się, że kod uruchamiany bezpośrednio z IDE (ja użyłem Eclipsa) działa jednak trochę wolniej, a przy takiej ilości danych różnica może być odczuwalna. Po zakończeniu sortowania pojawi się napis w konsoli.

 

App.java:

package org.kaisuj;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

/**

*/
public class App {

private static ArrayList originalList;

public static void main(String[] args) {
String fullFileName = System.getProperty(“user.dir”) + “/zrodlowy.txt”;

originalList = readFile(fullFileName);
Collections.sort(originalList, new StringLengthComparator());
writeSortedFile(originalList);
System.out.println(“Zakończono sortowanie”);
}

private static void writeSortedFile(ArrayList sorted){
String sortedFileName = System.getProperty(“user.dir”) + “/posortowany.txt”;
BufferedWriter outputWriter = null;
try {
outputWriter = new BufferedWriter(new FileWriter(sortedFileName));
for (int i = 0; i < sorted.size(); i++) {
outputWriter.write(sorted.get(i));
outputWriter.newLine();
}
outputWriter.flush();
outputWriter.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

private static ArrayList readFile(String fileName){
String line;
ArrayList lines = new ArrayList();
File file = new File(fileName);
BufferedReader br;
try {
br = new BufferedReader(new FileReader(file));
while ((line = br.readLine()) != null) {
lines.add(line);
}
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return lines;
}
}

StringLengthComparator.java:

package org.kaisuj;

import java.util.Comparator;

public class StringLengthComparator implements Comparator {

public int compare(String s1, String s2) {
if(s1.length() < s2.length()) {
return -1;
}
else if(s1.length() > s2.length()) {
return 1;
}
else {
return 0;
}
}
}

 

Dlaczego linijka “cat test2” jest jako pierwsza? zawiera aż 9 znaków (z spacją).

Reszta wygląda pięknie :slight_smile:

 

Ja w Javie się nie bawie, byłbym Ci wdzięczny za gotowego JARa pod konsole, jak byś dał rade.

Jak kojarze to uruchamiam tak?

java -jar 'ścieżka/plik.jar'

Czarno to widzę. Wrzucenie do listy 60 MB pliku to pikuś. Przy 6 GB pewnie poleci OutOfMemoryException (nawet jeśli komputer ma dużo więcej RAM-u). Przy takim zadaniu najlepiej byłoby zastosować podejście dziel i zwyciężaj. Nawet jeśli nie hadoopowy Map/Reduce z prawdziwego zdarzenia, to chociaż pliki tymczasowe (albo lepiej tabelki w bazie) przechowujące linie o jednakowej długości. W pamięci trzyma się ogarnialną liczbę linii, sortowanie alfabetyczne w obrębie jednego pliku będzie dużo lżejsze, a na końcu całość będzie banalna do zmerge’owania. Implementacja bardzo prosta, działanie nieobciążające zasobów.

 

Naskrobałem naiwną implementację w Rubym. Sama generacja pliku 1.2 GB trwała długo. Sam program będzie się baaaardzo długo wykonywać.

 

Natomiast mam tak pomysł - linuksowe Memory Mapped Files! Otwieranie pliku tak jakby był w pamięci ram. Do tego coś a la czytanie starego pliku linijka po linijce i wstawianie do nowego od razu posortowane (z użyciem wyszukiwania binarnego).

 

Dziel i zwyciężaj nic tu nie da. Implementacja sortowania w Javie już używa dziel i zwyciążaj.

Zrobiłem Ci gotowego jara. Możesz go pobrać z tego linku: https://mega.co.nz/#!H5BQTSDB!AbJDyffTjxeVz93IEw9ttQ545O2_TB2tNRXTqtzWbp8

Wywołanie tak, jak pisałeś, ale dodałem 2 parametry:

java -jar sciezka/FileSort.jar sciezka/plikZrodlowy sciezka/plikWynikowy

Jak nie podasz obu parametrów to nie pójdzie. 

 

Programik był napisany w Javie 8, więc musisz mieć u siebie zainstalowaną Jave 8.

Prawdopodobnie masz u siebie tylko OpenJDK w wersji 7. 

Tutaj http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html

masz do pobrania najnowsze JRE Javy. Jeśli pracujesz na Fedorze to jest RPM. Jeśli jednak Twoje distro nie pracuje na RPMach

to rozpakowany folder trzeba wrzucić do /usr/lib/jvm.

Po instalacji sprawdź jaką masz domyślnie ustawioną wersję javy w systemie, bo nie wiem czy instalacja z RPM domyślnie ustawi instalowaną wersję.

 

java -version

 

Powinno wyrzucić wersje 1.8.0_xx

 

Jeśli nie, to trzeba zmienić domyślną wersję:

 

U mnie zawsze działa taki zlepek poleceń:

 

sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.8.*/bin/java 1065 &&

LOL Myślałem że mając w nicku nazwę dystrybucji znasz polecenie cat i wiesz do czego jest wykorzystywane :wink:

Może to ci pomoże:

man cat

Nie LOL, tylko myślałem że nie chciało Ci się pisać dalej i wklepałeś jakieś polecenie jako nową linijke. Nie skojarzyłem tego jako

polecenia powłoki, może dlatego że ja czasem sie bawię historią basha(a) bo  nie chce mi sie tworzyc przykładowego pliku :smiley:

Moja zła interpretacja, uświadomiłem sobie to dopiero teraz… A jak cat działa to wiem.

 

 

 

Przetestowałem dla 6Gb dwie wersje:

a)perl -e ‘print sort {length $a <=> length $b} <>’ test.txt > test2.txt

b) i tego JARa

 

Wniosek: Bezporównania Perl widocznie wyprzedził Jave, na Javie zajeło 12 minut a Perlowi około 7-8 minut, puściłem je (polecenia) potrzym oba przerwałem CTRL+C, wyjsciowy perla miał 6Gb, a Javy 6GB, tak mniej wiecej. Bo plik 6Gb to dopiero pączatek :slight_smile:

Jeszcze czeka do posortowania 30Gb logów i raportów :frowning:

 

 

Jak ktoś ma coś co może sie przydać, to może mi podesłaś po przez wiadomość priv, Dziękuję wszystki,

a szczególnie tym co się chciało napisać program. Wykorzystam wersje A)

Teraz kwestia optymalizacji, bo chwilami się komputer przywiesił :smiley: Pomysły?

Nie jestem administratorem i się zbytnio na tym nie znam, ale kto trzyma logi w 30 gigabajtowych plikach tekstowych? Przecież tego się nie da nawet otworzyć do odczytu, nie mówiąc już o jakiejś edycji. Nie wiem skąd bierzesz te logi, ale chyba coś tam musisz mieć nie tak poustawiane.

Dobrze mam wszystko ustawione, a logów jest tyle ponieważ nasza firma rejestruję każdą aktywność klienta na jego bazie

danych, którą my przechowywujemy za jakąś tam opłatą (użyczamy przestrzeń dyskową i dbamy o bezpieczeństwo danych).

Trzymamy logi z dwóch lat, aby jak klient miał by do nas pretensje że coś mu u nas znikło, takie nasze zabezpieczenie.

Łatwo jest to analizować mimo takiej wielkośći (less, nano, grep), pozory mylą. Nic więcej nie moge powiedziec :slight_smile: