Re: Швидке сховище даних
Усе це легко може зробити БД. Достатньо просто записати у неї усі початкові дані з CSV-файлу. Але доведеться читати, що таке індекси, які вони бувають, та як працюють.
Ви не увійшли. Будь ласка, увійдіть або зареєструйтесь.
Ласкаво просимо вас на україномовний форум з програмування, веб-дизайну, SEO та всього пов'язаного з інтернетом та комп'ютерами.
Будемо вдячні, якщо ви поділитись посиланням на Replace.org.ua на інших ресурсах.
Для того щоб створювати теми та надсилати повідомлення вам потрібно Зареєструватись.
Український форум програмістів → Алгоритми та структури даних, технології → Швидке сховище даних
Для відправлення відповіді ви повинні увійти або зареєструватися
Усе це легко може зробити БД. Достатньо просто записати у неї усі початкові дані з CSV-файлу. Але доведеться читати, що таке індекси, які вони бувають, та як працюють.
Dim fs As FileStream = File.Open('big.csv', FileMode.Open)
Public Class FileSorter Implements IComparer
Public Function Compare( ByVal x As Object, ByVal y As Object) As Integer _ Implements IComparer.Compare
fs.Seek(x, SeekOrigin.Begin)
//читаємо потрібну змінну в x_value
fs.Seek(y, SeekOrigin.Begin)
//читаємо потрібну змінну в x_value
Return x<y
End Function
End Class
Якось так
Почитайте про incremental sorting
І як це допоможе? Чекаю на відповідь із попкорном.
Я тут подумав що можна з самого початку орієнтуватися не по номерам рядків а по їх зміщенням.
Вся ця затія зі зміщенням в байтах тепер видається мені дуже сумнівною. Використовую клас TextFieldParser. Для обрахунку довжини рядка використовую метод ReadLine який читає рядок як є не розбиваючи його по символам–розділювачам (; в моєму випадку). В документації вказано що «an end-of-line character within a delimited field is interpreted as the actual end of the line.
», отже якщо символ нового рядка є в тексті якогось 3 стовпчика з описом (ASDASD;123333;"Джукаа трьохдюймова йцвфілвофілволо \n asdsadsada";12.3;190\n), то текст зчитаються тільки до цього символу в третьому стовпчику і всі оці зміщення–адреси початку рядків будуть невірними і ніякої гарантії що зміщення 1029084 відповідає саме рядку №13450 а не його частині до першого символу нового рядка немає. Ще є метод PeekChars який читає задану кількість символів і повертає як рядок, але порожні рядки пропускаються і символи кінця рядку всередині полів інтерпретуються як кінець рядка (тобто така ж поведінка як у ReadLine). Для того щоб зчитати весь рядок невідомої довжини є хитрість: потрібно передати методу число яке є більшим ніж найдовший рядок.
Мені потрібно писати власний парсер або знайти такий який дозволяє зчитати рядок до останнього символу нового рядку який не не знаходиться всередині поля з даними всередині.
То що, ви вирішили проблему?
Взагалі csv - не найскладніший формат у світі, парсер не так складно зробити.
Ні. Схоже замовнику не особливо потрібно. А за початкову ціну в 300 гривень за сортування в алфавітному порядку в мене щось охота пропала. Створювати каталоги з файлами (яких виявилося близько 26 мільйонів) дуже довго (годин 7). І читати вміст усіх цих файлів теж дуже довго. Видалити це все добро теж виявилося проблемно. rm -rf в GNU/Linux виїдає всю оперативку після чого її вбивають. oMega commander довго рахує кількість файлів після чого починає довго видаляти. Якщо пробувати через командний рядок в Windows, то через деякий час виконання команди просто припиняється. Видалив написавши програму на Visual basic.NET
Console.WriteLine("Start {0}", System.DateTime.Now.ToLongTimeString())
System.IO.Directory.Delete("D:\\source\\duplicates remover\\duplicates remover\\bin\\Debug\\WPFRemover.offsets", True)
Console.ReadLine()
Console.WriteLine("End {0}", System.DateTime.Now.ToLongTimeString())
Це зайняло трохи більше трьох годин.
Потрібно зберегти структуру виду
1: 84, 2: 120, 456903: 587411
до якої потім можна буде звертатися по ключу. Для цього в .NET підходить словник (ключі типу Int64 і значення теж Int64), але записів може бути декілька мільйонів і в оперативну пам'ять воно не поміщається. Запис в таблицю RDBMS SQLite триває занадто довго. Можна якось так це все записати щоб потім не потрібно було весь файл завантажувати до ОП або шукати в файлі рядок з потрібним ключем? Щоб можна було просто вказати ключ і отримати потрібне значення?
Раджу використати msql з індексами. І все буде пучком. І з нею працювати.
Alchimic
Для цього треба знати, що таке індекси, та як ними користуватися. Автор цей варіант навіть не розглядає, я уже пропонував.
Швидке сховище це ліст в оператвці. Або бд на рам диску.Потрібен сервер з 64 гб. Або бд на сд диску. Це дешевше.
Функція sortAndStore читаю з файлу в якому є два поля через кому, з сортуванням по першому полю, та записує в новий файл з сортуванням вже по другому полю, файли source.csv, destination.csv
використав приклад для відображення пам'яті https://golangcode.com/print-the-current-memory-usage/
Для 1 000 000 рядків:
Alloc = 28 MiB TotalAlloc = 199 MiB Sys = 60 MiB NumGC = 30
PASS
ok _/home/senseye/develop/projects/BigSort 4.806s
Для 5 000 000 рядків:
Alloc = 151 MiB TotalAlloc = 976 MiB Sys = 299 MiB NumGC = 43
PASS
ok _/home/senseye/develop/projects/BigSort 25.129s
Код з тестом, щоб без помилок:
package bigsort
import (
"bufio"
"fmt"
"math/rand"
"os"
"runtime"
"sort"
"strconv"
"strings"
"testing"
)
type (
item struct {
member int
score int
}
)
const (
sourceFileName = "source.csv"
destinationFileName = "destination.csv"
size = 1e6
)
func Test(t *testing.T) {
err := fillFileOnce(sourceFileName, size)
assertSuccess(t, err)
err = sortAndStore(sourceFileName, destinationFileName)
assertSuccess(t, err)
printMemUsage()
}
func assertSuccess(t *testing.T, err error) {
if err != nil {
t.Fatal("Expect success, but got:", err)
}
}
func fillFileOnce(fileName string, count int) error {
if f, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0600); err == nil {
f.Close()
return nil
}
f, err := os.Create(fileName)
if err != nil {
return err
}
f.Close()
f, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
return err
}
defer f.Close()
perm := rand.Perm(count)
for i, v := range perm {
f.Write([]byte(fmt.Sprintf("%d,%d\n", i, v)))
}
return nil
}
func sortAndStore(sourceFileName, destinationFileName string) error {
if f, err := os.OpenFile(destinationFileName, os.O_APPEND|os.O_WRONLY, 0600); err == nil {
f.Close()
return nil
}
sourceFile, err := os.OpenFile(sourceFileName, os.O_RDONLY, 0600)
if err != nil {
return err
}
defer sourceFile.Close()
f, err := os.Create(destinationFileName)
if err != nil {
return err
}
f.Close()
destinationFile, err := os.OpenFile(destinationFileName, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
return err
}
defer destinationFile.Close()
items := make([]item, 0)
scanner := bufio.NewScanner(sourceFile)
for scanner.Scan() {
parts := strings.Split(scanner.Text(), ",")
items = append(items, item{
member: parseInt(parts[0]),
score: parseInt(parts[1]),
})
}
sort.Slice(items, func(i, j int) bool { return items[i].score < items[j].score })
for i := range items {
destinationFile.Write([]byte(fmt.Sprintf("%d,%d\n", items[i].member, items[i].score)))
}
return nil
}
func parseInt(s string) int {
r, _ := strconv.Atoi(s)
return r
}
func printMemUsage() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
// For info on each, see: https://golang.org/pkg/runtime/#MemStats
fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
fmt.Printf("\tNumGC = %v\n", m.NumGC)
}
func bToMb(b uint64) uint64 {
return b / 1024 / 1024
}
Для відправлення відповіді ви повинні увійти або зареєструватися