konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

Witam.
1. Mam taki mały problem, staram się zrobić program który wrzuci zawartość z góry określonego folderu na serwer FTP, jednak mam problem w wypadku, gdy chcę z tego folderu skopiować wszystkie pliki, jakie tylko się w nim znajdują. Z uploadem jednego pliku, nie mam najmniejszego problemu, jednak jak to wykonać w wypadku kopiowania całej zawartości danego folderu?

2. Druga sprawa to synonimy z tekstu. Mam np zdanie "ala ma {psa|kota|mysze} w domu" i chodzi mi o to, jak zrobić, aby wylosować tylko jeden synonim z tego zdania, żeby np było "ala ma kota","ala ma psa" etc...

Z góry dziękuję za pomoc :)Paweł Cyrklaf edytował(a) ten post dnia 09.09.11 o godzinie 04:12
Remigiusz Towalski

Remigiusz Towalski Manager R&D
Engineering

Temat: Upload na FTP, oraz synonimy w tekście

1. Pętla po plikach z tego folderu?

2. Poczytaj o klasie System.Random.

Temat: Upload na FTP, oraz synonimy w tekście

Paweł Cyrklaf:
a to synonimy z tekstu. Mam np zdanie "ala ma
{psa|kota|mysze} w domu" i chodzi mi o to, jak zrobić, aby wylosować tylko jeden synonim z tego zdania, żeby np było "ala ma kota","ala ma psa" etc...

Jak długi jest to tekst? Chodzi o optymalizację. Jeśli krótki, to poniższa wersja powinna wystarczyć:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace test
{
class Program
{
static void Main(string[] args)
{
// 10 kolekcji synonimów
string text = @"Ala ma {psa|kota|mysz} a Zosia {tasiemca|lamblie|owsiki}.
Czy {Ala|Zosia} ma powód do {radości|smutku}? Jest to trudne filozoficznie pytanie.
Od {setek|tysięcy|milionów} lat {socjologowie|filozofowie|matematycy|fizycy} starają się
odpowiedzieć na to {złożone|skomplikowane|nietrywialne} pytanie. {W końcu|Ostatecznie}
Zosia ma {stworzenie|zwierzątko} w sobie, a {więc|zatem} jest ono bliższe jej sercu.";

Console.WriteLine(ReplaceWithSynonym(text));
Console.ReadKey();

int repeats = 100;
Console.WriteLine("\nPrzetwarzanie " + (repeats * text.Length) + " znaków i " + repeats*10 + " kolekcji synonimów. Start!");
Console.WriteLine("Przetwarzanie zajęło " + TestYourMight(text, repeats) + " sekund");
Console.ReadKey();
repeats = 1000;
Console.WriteLine("\nPrzetwarzanie " + (repeats * text.Length) + " znaków i " + repeats * 10 + " kolekcji synonimów. Start!");
Console.WriteLine("Przetwarzanie zajęło " + TestYourMight(text, repeats) + " sekund");
Console.ReadKey();
}

private static string ReplaceWithSynonym(string sourceText)
{
Random rnd = new Random();
StringBuilder src = new StringBuilder(sourceText);

string synonymsCollectionDef = @"{.*?}";
Regex regex = new Regex(synonymsCollectionDef, RegexOptions.Singleline | RegexOptions.IgnoreCase);

Match match = regex.Match(sourceText);
while (match.Success)
{
string[] synonyms = match.Value.Trim(new char[] { '{', '}' }).Split('|');

src.Replace(match.Value, synonyms[rnd.Next(synonyms.Length)]);

match = match.NextMatch();
}
return src.ToString();
}

private static string TestYourMight(string template, int repeats)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < repeats; i++)
{
sb.Append(template);
}

DateTime startTime = DateTime.Now;
ReplaceWithSynonym(sb.ToString());
return (DateTime.Now - startTime).TotalSeconds.ToString();
}
}

}


Dla 400 tys. znaków w tekście i 10 tys. kolekcji synonimów w nim przetwarzanie zajęło 27 sek. Dla 40 tys. znaków i 1000 kolekcji - 0.33 sek. Do prostych zastosowań chyba wystarczy.


Obrazek


I małe testy wydajnościowe. Złożoność wielomianowa, kwadratowa:
Obrazek
Adrian Olszewski edytował(a) ten post dnia 09.09.11 o godzinie 12:45

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

`Piotr Sowa edytował(a) ten post dnia 12.02.12 o godzinie 16:33

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

Piotrek i Adrian: nieźle, ale proszę wpierw formalny dowód poprawności algorytmu. Co mi po fajnym i wydajnym kodzie, kiedy nie wiem czy poprawny;)

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

`Piotr Sowa edytował(a) ten post dnia 12.02.12 o godzinie 16:13

Temat: Upload na FTP, oraz synonimy w tekście

Ale czego ja mam dowodzić? :)

Regex wyszykuje wszystkie literały między podanymi separatorami. Zakładam, że działa poprawnie :) Znalezione literały to listy synonimów. Pętla przechodzi po znalezionych listach i dzieli je względem podanego separatora na synonimy. Funkcja dzieląca również jest wbudowana, więc zakładam, że działa poprawnie. Przed podziałem literał jest oczyszczany z wiodącego i końcowego separatora. j.w. W tekście źródłowym definicja synonimów (literał od { do }) zamieniamy jest na losowo wybrany z dostępnej aktualnie puli synonim. Funkcja Replace i Next - j.w. Jest to najzwyklejsze find/replace.

Trzeba do tego wyciągania zeszytu ze studiów, indukcji, szukania niezmienników i dowodu zbieżności (iteracje po skończonych listach elementów) ? :)

----------

A tymczasem... :)

Obrazek


Ale to już nie jest interesujące. Ot - kombinowanie w pętli, jak na laborkach. W dodatku takie rozwiązanie jest bardziej podatne na błędy. To z Regexem ma jedną zaletę - przy niewielkiej ilości danych (gdy jeszcze jest dostatecznie szybkie) jest intuicyjne i proste, można dowolnie zmieniać separatory na wieloznakowe. To jest jeden z przykładów, gdy gorsze obliczeniowo rozwiązanie jest lepsze z innych względów - mniejsza podatność na błędy, czytelność, łatwość modyfikacji.

PS: jeszcze dla miliona kolekcji - 6.4 sek.

Obrazek


To rozwiązanie ma jednak wadę - jest nieodporne na niewłaściwy format danych. A dodawanie kolejnych warunków bardzo zaciemni obraz, więc - z czystego lenistwa, zakładam, że tekst ma właściwy format, tzn. że delimitery definicji są sparowane.

-----------------------
EDIT: Oto kod. Założenie - ŻADNYCH "replace", żadnych "IndexOf", jedynie
dopisywanie na końcu stringu i to za pomocą StringBuildera. Gdyby ktoś miał pomysł, jak to ładniej zapisać...

private static string ReplaceWithSynonyms(string sourceText)
{
Random rnd = new Random();

// synonimy dostępne w obrębie bieżącej definicji
List<string> synonyms = new List<string>();

bool readSynonym = false;
string synonym=string.Empty;

// Tak, wiem, że można wziąć synonyms.Count, ale - żadnych
// dodatkowych pętli :]
int synonymsCount = 0;

StringBuilder newText = new StringBuilder();

for (int i = 0; i < sourceText.Length; i++)
{
// jeśli delimiter początku definicji...
if (sourceText[i] == '{')
{
// siostro, mamy synonim.
synonymsCount++;

// W next iteracji będziemy
// traktować znaki z wejścia jak znaki słowa synonimu.
readSynonym = true;
continue;
}

if (sourceText[i] == '}')
{
// jeszcze cieplutki synonim do kolekcji dostępnych
// w zakresie tej definicji
synonyms.Add(synonym);

// Na razie koniec. Czyścimy, zamiatamy. Wyłączamy tryb
// "synonim detected". Od tej pory znaki z wejścia
// będziemy zwyczajnie przepisywać na wyjście.
synonym = string.Empty;
readSynonym = false;

// do dotychczasowego tekstu doklejamy jeden z synonimów
newText.Append(synonyms[rnd.Next(synonymsCount)]);

// taaak, to może być czasochłonne. Można to ominąć.
// Dodatkowe indeksy "lower bound" i "upper bound" dla
// funkcji rnd.Next(lower, upper) załatwią problem -
// nie trzeba czyścić listy, tylko do niej dopisywać.
synonyms.Clear();

// TAK, wiem, że w tej chwili synonyms.Count == 0 :]
synonymsCount = 0;

continue;
}

// Tryb czytania synonimu
if (readSynonym)
{
// bo beleczka mogła być składową tekstu
// TAK, wiem, nie zaimplementowałem tego dla { i } :]
if (sourceText[i] == '|')
{
// czytanie bieżącego synonimu - zakończone.
synonyms.Add(synonym);
synonym = string.Empty;

// a skoro była beleczka, to mamy next synonim
synonymsCount++;
continue;
}

// kolejne znaki są częścią słowa - synonimu.
synonym += sourceText[i];
continue;
}

// nie jesteśmy w trybie "synonim detected", więc przepisujemy
// znaki z wejścia na wyjście.

newText.Append(sourceText[i]);
}

return newText.ToString();
}


O(n). Jak się "rozkręciło", to nawet 5.6 sek. Niższego rzędu już nie będzie (jednokrotne przejście przez zbiór danych; Clear() -pętla- nie jest operacją dominującą), ale może ktoś jeszcze zmniejszy stałą.


Obrazek
Adrian Olszewski edytował(a) ten post dnia 10.09.11 o godzinie 00:22

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

Remigiusz Towalski:
1. Pętla po plikach z tego folderu?

2. Poczytaj o klasie System.Random.
Tak, chodzi o to aby wszystkie pliki z danego folderu skopiował na serwer ftp, czyli musiałaby być pętla i raczej w niej upload na serwer ftp.

Dziękuję wszystkim za dotychczasowe odpowiedzi:)

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

`Piotr Sowa edytował(a) ten post dnia 12.02.12 o godzinie 16:33

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

`Piotr Sowa edytował(a) ten post dnia 12.02.12 o godzinie 16:33

Temat: Upload na FTP, oraz synonimy w tekście

No Piotrze, szacun :) Teraz to już tylko można wątki sprostopadlić i wykona się w czasie ujemnym :P

Myślę, że spokojnie można jeszcze usunąć Clear() listy synonimów na rzecz operowania dwoma indeksami, które byłyby podawane do metody Next(). Wtedy jedynie dopisujesz nowe synonimy do kolekcji, nie czyścisz jej, a random generuje liczby z odpowiedniego przedziału, właściwego dla danej definicji.

Jakim ja cudem do budowania synonimu użyłem String zamiast StringBulder - nie mam pojęcia... Jedynym wytłumaczeniem może być późna pora :) Wystarczyła tylko ta jedna prosta zmiana (zerowanie SB przez wyzerowanie Length) i czasy spadły o połowę.

kolekcji	100000	milisekund	313
kolekcji 200000 milisekund 609
kolekcji 300000 milisekund 906
kolekcji 400000 milisekund 1250
kolekcji 500000 milisekund 1531
kolekcji 600000 milisekund 1813
kolekcji 700000 milisekund 2313
kolekcji 800000 milisekund 2453
kolekcji 900000 milisekund 2734
kolekcji 1000000 milisekund 3094


A swoją drogą - w 3.5 nie ma Clear dla StringBuildera... A ja piszę właśnie pod tę wersję i jeszcze długo nie przestawię się na VS2010/4.0 (odpycha mnie graficznie i wydajnościowo jak diabli). Może się komuś przyda: jak czyścić StringBuilder

A co do porównywania czasów - trochę to bez sensu robimy, choć wyniki mamy zbliżone. Musielibyśmy mieć identyczne środowiska. Pracuję na dość wysłużonym lapku Lenovo (R61i, XP, 2GB, Core2Duo 1.66), a w tle mam nauruchamiane od groma programów.

Obrazek


Najważniejszy jest rząd wielkości i stosunki stałych/czasów (a nie surowe wartości stałych) sprzed i po zmianie.Adrian Olszewski edytował(a) ten post dnia 10.09.11 o godzinie 13:51

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

`Piotr Sowa edytował(a) ten post dnia 12.02.12 o godzinie 16:33

Temat: Upload na FTP, oraz synonimy w tekście

No właśnie. Kretyn ze mnie. Pomiary robiłem w trybie Debug :]
W Release:
kolekcji;900000;milisekund;2468,75
kolekcji;1000000;milisekund;2765,625

I to wszystko, co wyciągnę na v.3.5

Przy okazji - zastąpienie Clear'owania listy synonimów na parę indeksów dolny/górny dało... gorszy wynik (sprawdzane wielokrotnie). Być może przez operacje arytmetyczne w Random(from, to).


Obrazek

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

`Piotr Sowa edytował(a) ten post dnia 12.02.12 o godzinie 16:32

Temat: Upload na FTP, oraz synonimy w tekście

Nieee, to już za dużo roboty :P My tu o milionach synonimów, a kolega pewnie chciał z 50 i 5-linijkowe rozwiązanie z regexem pewnie spokojnie wystarczy :)

Swoją drogą przy okazji tej dyskusji związanej z parsowaniem tekstu, w którym znajdują się jakieś "definicje", rozwinąłem narzędzie "wdrożeniowe" do firmy, które przyjmuje na wejściu SQL i tworzy z niego CSV. Rzecz w tym, że te nasze zapytania mają dużo parametrów i w różnych miejscach (SELECT, JOIN, WHERE, HAVING). Fajnie jest móc edytować ich wartości wprost z aplikacji migratora. Czemu zatem nie opisać tych parametrów metadanymi i na ich podstawie wyświetlić odpowiednie pola do uzupełnienia?

Gdyby był ktoś zainteresowany, utworzę temat i wrzucę na forum kod kilku funkcji, które odwalają czarną robotę. Jedna jest podobna do powyższej z Regexem, a druga parsuje fragment XML (bo tym właśnie jest definicja parametru - XmlNode) i wyciąga z niego różne informacje.

Wygląda to tak (czerwona obwódka):

Obrazek


A w skrypcie SQL np. tak:
SELECT ........
FROM ........

WHERE v.visit_date > to_date(<param Name='Data wizyty' DefValue='2000-01-01' Global='N' Type='Date'/>, 'yyyy-mm-dd')
AND v.hcc_name LIKE <param Name='Nazwa HCC' DefValue='%iMed24' Global='N' Type='Text'/>
AND lpd.name IN <param Name='Profile badań' DefValue='Cholesterol całkowity|Morfologia krwi (podstawowa)|Glukoza' Global='N' Type='TextList'/>
AND oper.gender = <param Name='Płeć pacjenta' DefValue='M' Global='N' Type='Text'/>
AND oper.birthday > to_date(<param Name='Data ur. pacj.' DefValue='23-03-2009|dd-MM-yyyy' Global='N' Type='Date'/>, 'dd-mm-yyyy')
AND oper.active = <param Name='Aktywność' DefValue='Y' Global='Y' Type='Text' Multiple='Y'/>


lub tak:
SELECT  CASE WHEN oper.gender = <param Name='Płeć (stary kod)' DefValue='K' Global='N' Type='Text'/> THEN <param Name='Płeć (nowy kod)' DefValue='F' Global='N' Type='Text'/> 
ELSE oper.gender
END
FROM ...... oper


Ogólny format:
<param Name='nazwa parametru’ DefValue='wartość domyślna|format' Global='Y' Type='typ' Multiple='Y'/>

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

`Piotr Sowa edytował(a) ten post dnia 12.02.12 o godzinie 16:32

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

hehe ładnie się rozpisaliście, zastosowaliście ciekawe rozwiązania :) choć mi chodziło o najprościejsze losowanie synonimów ;)

konto usunięte

Temat: Upload na FTP, oraz synonimy w tekście

Synonimy działają elegancko, dziękuję za pomoc :)

Czy mógłby mi ktoś pomóc z tym Uploadem. Upload folderu gdzie nie ma innych folderów zrobiłem bez problemu, tylko jak zrobić, aby np skopiował zawartość danego folderu, czyli wszystkie pliki oraz foldery z niego na FTP, i na serwerze FTP utworzył taką samą strukturę.

Z góry dziękuję za pomoc, i przepraszam za post pod postem.

Temat: Upload na FTP, oraz synonimy w tekście

Ale z czym konkretnie masz problem? Z rekurencją?

Tak na szybko - dwie funkcje, które kopiują zawartość katalogu z "from" do "to". Ta pierwsza jest napisana przez MS (pamiętaj o dodaniu referencji do Microsoft.VisualBasic) i, jak widać, bardzo prosto się ją wywołuje, często z niej korzystam. Ta druga to zwykła funkcja rekurencyjna, którą można "przykroić" do swoich potrzeb, np. dodać raportowanie postępu.

Testowane na lokalnym filesystemie, przerób sobie na FTP i dodaj obsługę błędów sieciowych.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic;
using System.IO;

namespace DirCopy
{
class Program
{
static void Main(string[] args)
{
CopyWithVB(@"R:\dirs\source", @"R:\dirs\target_VB");

CopyWithRecursion(new DirectoryInfo(@"R:\dirs\source"), new DirectoryInfo(@"R:\dirs\target_recursion"));
}

static void CopyWithVB(string from, string to)
{
Microsoft.VisualBasic.Devices.Computer comp = new Microsoft.VisualBasic.Devices.Computer();
comp.FileSystem.CopyDirectory(from, to);
}

static void CopyWithRecursion(DirectoryInfo from, DirectoryInfo to)
{
if (! Directory.Exists(to.FullName))
{
Directory.CreateDirectory(to.FullName);
}

foreach (FileInfo finfo in from.GetFiles())
{
finfo.CopyTo(Path.Combine(to.FullName.ToString(), finfo.Name), true);
}

foreach (DirectoryInfo subdir in from.GetDirectories())
{
CopyWithRecursion(subdir, to.CreateSubdirectory(subdir.Name));
}
}
}
}


I wynik:
Obrazek


-----------
EDIT: Jeszcze jedna możliwość, kompromis pomiędzy obiema powyższymi: banalnie prosta - wszystko dzieje się w jednej pętli, rekurencję odwala za Ciebie GetDirectories(), a jednocześnie można ją łatwo rozbudować o raport postępu:

static void CopyWithSingleLoop(string from, string to)
{
foreach (string dir in Directory.GetDirectories(from, "*.*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(dir.Replace(from, to));
}

foreach (string file in Directory.GetFiles(from, "*.*", SearchOption.AllDirectories))
{
File.Copy(file, file.Replace(from, to));
}
}


i wywołanie:
CopyWithSingleLoop(@"R:\dirs\source", @"R:\dirs\target_singleloop");
Adrian Olszewski edytował(a) ten post dnia 26.09.11 o godzinie 15:33



Wyślij zaproszenie do