Grzegorz P.

Grzegorz P. Programista .NET
ASP.NET Silverlight
MCP, MCTS

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Witam!

Czy znacie darmową bibliotekę / wzorzec / algorytm, który zamieni mi długą listę podobnych stringów na strukturę drzewiastą?

Wymagania:
- współpraca z c# i VS 2008/VS 2010.
- rzeczywista lista ma około 10.000 pozycji.
- rozsądny czas przetwarzania około 10 tys. wierszy

Dane wejściowe:
------------------------

"Kolano żeliwne F100A"
"Kolano żeliwne F100B"
"Kolano żeliwne F100C"
"Kolano żeliwne F200"
"Kolano pcv F100A"
"Kolano pcv F100B"
"Kolano pcv F200A"

----------------------

Dane na wyjściu (drzewko):

Kolano
->żeliwne
->F100
-> A
-> B
->F200
->pcv
->F100
-> A
-> B
->F200
-> A

Pozdrawiam z nad morza.
GrzessiekkGrzegorz Pawluch edytował(a) ten post dnia 06.08.10 o godzinie 13:54

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Do zbudowania struktury drzewiastej możesz użyć wzorca Visitor http://en.wikipedia.org/wiki/Visitor_pattern

Jeśli byś miał jakieś problemy z implementacją to na forum SO jest przykład w Javie: http://stackoverflow.com/questions/1005551/construct-a...

Pozdrawiam

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Może coś takiego:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Test
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}


private void button3_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Kolano żeliwne F100A");
sb.AppendLine("Kolano żeliwne F100B");
sb.AppendLine("Kolano żeliwne F100C");
sb.AppendLine("Kolano żeliwne F200");
sb.AppendLine("Kolano pcv F100A");
sb.AppendLine("Kolano pcv F100B");
sb.AppendLine("Kolano pcv F100C");
TreeNode tree = new TreeNode();
string[] lines = System.Text.RegularExpressions.Regex.Split(sb.ToString(), @"\r\n", System.Text.RegularExpressions.RegexOptions.Multiline | System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Compiled);
for(int i = 0; i < lines.Length; i ++)
{
string[] nodes = System.Text.RegularExpressions.Regex.Split(lines[i], " ", System.Text.RegularExpressions.RegexOptions.Multiline | System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Compiled);
if (nodes.Length > 1)
{
tree.AddNodes(nodes);
}
}
}
}
public class TreeNode
{
public TreeNode Parent;
public List<TreeNode> Nodes;
private Dictionary<string,int> nodeIndex;
public string Name;
public string Data;
public TreeNode()
{
this.Nodes = new List<TreeNode>();
this.nodeIndex = new Dictionary<string,int>();
this.Name = this.Data = null;
}

public TreeNode(TreeNode parent, string name, string data):this()
{
this.Parent = parent;
this.Name = name;
this.Data = data;
}
public void AddNode(string name)
{
if (!this.nodeIndex.ContainsKey(name))
{
this.Nodes.Add(new TreeNode(this, name, "<data>"));
this.nodeIndex.Add(name, this.Nodes.Count -1);
}
}
public void AddNodes(string[] nodes)
{
TreeNode parent = this;
if (!this.nodeIndex.ContainsKey(nodes[0]))
{
this.AddNode(nodes[0]);
}

parent = this.Nodes[this.nodeIndex[nodes[0]]];
if (nodes.Length > 1)
{
string[] nn = new string[nodes.Length - 1];
Array.Copy(nodes, 1, nn, 0, nn.Length);
parent.AddNodes(nn);
}
}

public override string ToString()
{
return String.Format("{0}:{1}", this.Name, this.Data);
}
}
}
Sergiusz B. edytował(a) ten post dnia 07.08.10 o godzinie 14:12

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Zmieniłbym tylko w rozwiązaniu Sergiusza metodę dzielenia linii i wyrazów na:

string[][] chunks = sb
.ToString()
.Split(new[] { Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split(' ')).ToArray();


Używanie wyrażeń regularnych do takich czynności to straszne marnotrawstwo procesora i pamięci ;)Karim A. edytował(a) ten post dnia 08.08.10 o godzinie 03:21

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Karim A.:

Używanie wyrażeń regularnych do takich czynności to straszne marnotrawstwo procesora i pamięci ;)Karim A. edytował(a) ten post dnia 08.08.10 o godzinie 03:21

Regex ma taką kiepską implementację w .NET? Przyznam się, że jakoś tego nigdy nie testowałem mocno, z doświadczeń unixowych z kolej mam tak, że jak tylko gdzieś coś do wykonania jest na tekście w dużych ilościach to tylko wyrażenia regularne :)

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Jak jest zaimplementowana metoda "split", która występuje i tu, i tu? Nie "regexowo"? ;)

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Adrian Olszewski:
Jak jest zaimplementowana metoda "split", która występuje i tu, i tu? Nie "regexowo"? ;)

W implementacji split nie są używane wyrażenia regularne. Rękodzieło + unsafe code :).

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Jarosław D.:
Adrian Olszewski:
Jak jest zaimplementowana metoda "split", która występuje i tu, i tu? Nie "regexowo"? ;)

W implementacji split nie są używane wyrażenia regularne. Rękodzieło + unsafe code :).
Egzaktli!

Wyborażasz sobie parsowanie CSV regexami? ;)

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Karim A.:

Wyborażasz sobie parsowanie CSV regexami? ;)

Sam nie wiem. Pod terminem "parsowanie" może kryć się dużo i jednocześnie nic :DJarosław D. edytował(a) ten post dnia 08.08.10 o godzinie 22:25

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

A czym parsować jak nie regexami :) Przecież po to zostały wymyślone.

Grzegorz: może zamieścisz kawałek tych danych wejściowych (jeśli nie są jakieś top-secret), można byłoby zrobić test wydajności dla splita i regexa?

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Sergiusz B.:
A czym parsować jak nie regexami :) Przecież po to zostały wymyślone.

Akurat jeśli chodzi o CSV to jest to dla mnie poroniony pomysł.
Głównie przez escapowanie i NL.

Samodzielnie wystrugany kod to pewny, jedno-ekranowy automat stanowy.
Do regexpa miałbym w tym wypadku mniejsze zaufanie.

Ale oczywiście na pewno ktoś to już zrobił...

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Piotr Likus:
Sergiusz B.:
A czym parsować jak nie regexami :) Przecież po to zostały wymyślone.

Akurat jeśli chodzi o CSV to jest to dla mnie poroniony pomysł.
Głównie przez escapowanie i NL.

Samodzielnie wystrugany kod to pewny, jedno-ekranowy automat stanowy.
Do regexpa miałbym w tym wypadku mniejsze zaufanie.

... i przede wszystkim, bardziej optymalny.

http://stackoverflow.com/questions/998997/should-i-avo...

Szczególnie podoba mi się wypowiedź: http://stackoverflow.com/questions/998997/should-i-avo...

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Piotr Likus:

Akurat jeśli chodzi o CSV to jest to dla mnie poroniony pomysł.
Głównie przez escapowanie i NL.

To jest pewnie kwestia znajomości Regexpów - napisać dobry pattern to nie takie proste no i doświadczeń - dla mnie jeśli chodzi o przetwarzanie dużej ilości tekstu są niezastąpione.

Split jest znacznie bardziej prostszy do wykorzystania i w tym jest jego główna zaleta. Co do wydajności to bez konkretnych liczb dyskusja wydaje się być bezcelowa.
Karim A.:

Szczególnie podoba mi się wypowiedź: http://stackoverflow.com/questions/998997/should-i-avo...

Dokładnie - regexp nie jest panaceum na wszystkie zadania: niektóre ułatwia, w niektórych jest armatą na muchę.

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Sergiusz B.:
Co do wydajności to bez konkretnych liczb dyskusja wydaje się być bezcelowa.

Zgodzę się :)


static void Main(string[] args)
{
Stopwatch timer = new Stopwatch();
string testString = File.ReadAllText("test.txt");

string[] result;

timer.Start();
for(int i = 0; i < 10000; i++)
result = System.Text.RegularExpressions.Regex.Split(testString, @"\r\n", System.Text.RegularExpressions.RegexOptions.Multiline | System.Text.RegularExpressions.RegexOptions.IgnoreCase | System.Text.RegularExpressions.RegexOptions.Compiled);
timer.Stop();

TimeSpan regexTime = timer.Elapsed;
timer.Reset();


timer.Start();
for (int i = 0; i < 10000; i++)
result = testString.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
timer.Stop();
TimeSpan stringSplitTime = timer.Elapsed;

Console.WriteLine("Regex: {0}", regexTime.TotalMilliseconds);
Console.WriteLine("StrSplit: {0}", stringSplitTime.TotalMilliseconds);
Console.ReadLine();
}


test.txt:


Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Praesent a orci ante, non tincidunt augue.
Aenean sit amet nunc in ante aliquet molestie nec vitae libero.
Pellentesque in magna sapien, vitae auctor libero.
Quisque tincidunt turpis non velit vestibulum eleifend.
Duis cursus felis sit amet quam pulvinar egestas.
Nullam ut diam feugiat risus commodo semper.
Vestibulum sed ante a dolor lobortis aliquet non eu diam.
Aliquam fermentum mauris ac purus ullamcorper quis fermentum felis dignissim.
Donec feugiat ligula a eros hendrerit egestas.
Donec tempor accumsan augue, venenatis bibendum nulla cursus sit amet.
Ut congue neque id elit porttitor hendrerit.
Maecenas vel odio vel tortor vestibulum dignissim.
Quisque id orci augue, eget rhoncus elit.
Maecenas rhoncus dui id nunc mollis vel accumsan lacus porta.
Etiam congue posuere justo, ac feugiat mauris consequat vel.
Vestibulum eu magna elit, ac fringilla arcu.
Nam egestas tincidunt ligula, vel hendrerit ligula pretium ullamcorper.
Ut hendrerit lorem et velit imperdiet non pulvinar nibh vulputate.
Quisque sit amet ipsum eu purus interdum luctus.
Cras pharetra ligula non sem mattis vehicula.
In condimentum mi interdum dui fermentum sit amet rutrum sem sodales.
Suspendisse egestas arcu eget nisi rhoncus in rutrum augue molestie.
Sed congue neque ut nisi condimentum suscipit congue risus iaculis.
Phasellus ut enim ipsum, in placerat nisl.
Cras in urna sed dui sollicitudin accumsan.
Mauris luctus sagittis elit, et viverra mi bibendum at.
Integer ut quam dolor, a molestie tellus.


Wynik działania:

Regex: 850,4706
StrSplit: 317,4909


String.Split prawie 3x wydajniejszy ;-)

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Jeżeli pod "parsowaniem" csv ma się kryć tylko split na linie i split na kolumny, to tak jak napisał wcześniej Karim, nie potrzeba (a nawet nie należy) stosować do tego wyrażeń regularnych.

Edit: to rozwinięcie powinno znaleźć się w mojej ostatniej wypowiedzi.Jarosław D. edytował(a) ten post dnia 09.08.10 o godzinie 12:43

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Czyli mamy zwyciężce :). Jestem ciekaw jak zresztą - czy autor postu miał okazję przetestować drzewko na swoich danych.
Grzegorz P.

Grzegorz P. Programista .NET
ASP.NET Silverlight
MCP, MCTS

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Witam!

Dziękuję za odzew, przekraczający moje marzenia :) Dziękować :)

W moja lista towarów, którą chcę przekształcić do drzewa jest bardzo zróżnicowana i aktualizowana (czasami kilka razy dziennie).

Nigdy nie wiem co będzie zawierać lista towarów. Jedynie co wiem, to, że jest pewne podobieństwo miedzy wierszami (wg przykładu), nigdy nie wiem ile będzie poziomów gałęzi.

Moim cele jest zbudowanie zagnieżdżonego drzewa kategorii.

Niedługo przetestuje Wasze rady i dam znać.

Zastanawia mnie jeszcze jeden problem, jak już podzielę każdy wiersz na stringi, to muszę z nich zbudować drzewko :) czasowo efektywnie :)

Pozdrawiam
GPGrzegorz Pawluch edytował(a) ten post dnia 12.08.10 o godzinie 14:45

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Karim A.:
Sergiusz B.:
Co do wydajności to bez konkretnych liczb dyskusja wydaje się być bezcelowa.

Zgodzę się :)

(...)

Wynik działania:

Regex: 850,4706
StrSplit: 317,4909


String.Split prawie 3x wydajniejszy ;-)


Wyrażenia regularne potrafią być wydajne zarówno w tych mniej, jak i bardziej skomplikowanych przypadkach. Problem z nimi jest taki, że jeśli się nie zna dobrze składni i nie wie, chociaż pobieżnie, jak to wszystko działa pod maską, to bardzo łatwo jest wdepnąć w wydajnościową dziurę.

Dla porównania taki kod:
static void Main( string[] args )
{
Stopwatch timer = new Stopwatch();
string testString = File.ReadAllText(Environment.GetFolderPath( Environment.SpecialFolder.Desktop ) + "\\text.txt");

string[] result;


timer.Start();

Regex regex = new Regex( @"\r\n", RegexOptions.Compiled );
for( int i = 0; i < 30000; i++ )
result = regex.Split( testString );

timer.Stop();

TimeSpan regexTime = timer.Elapsed;
timer.Reset();


timer.Start();

for (int i = 0; i < 30000; i++)
result = testString.Split(new[] { Environment.NewLine }, StringSplitOptions.None);

timer.Stop();

TimeSpan stringSplitTime = timer.Elapsed;


Console.WriteLine("Regex: {0}", regexTime.TotalMilliseconds);
Console.WriteLine("StrSplit: {0}", stringSplitTime.TotalMilliseconds);
Console.ReadLine();
}


I mam nieznaczną przewagę regexa :)

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Dawid Zawada:
Karim A.:
Sergiusz B.:
Co do wydajności to bez konkretnych liczb dyskusja wydaje się być bezcelowa.

Zgodzę się :)

(...)

Wynik działania:

Regex: 850,4706
StrSplit: 317,4909


String.Split prawie 3x wydajniejszy ;-)


Wyrażenia regularne potrafią być wydajne zarówno w tych mniej, jak i bardziej skomplikowanych przypadkach. Problem z nimi jest taki, że jeśli się nie zna dobrze składni i nie wie, chociaż pobieżnie, jak to wszystko działa pod maską, to bardzo łatwo jest wdepnąć w wydajnościową dziurę.

Dla porównania taki kod:
static void Main( string[] args )
{
Stopwatch timer = new Stopwatch();
string testString = File.ReadAllText(Environment.GetFolderPath( Environment.SpecialFolder.Desktop ) + "\\text.txt");

string[] result;


timer.Start();

Regex regex = new Regex( @"\r\n", RegexOptions.Compiled );
for( int i = 0; i < 30000; i++ )[/quote]> result = regex.Split( testString );[quote]
timer.Stop();

TimeSpan regexTime = timer.Elapsed;
timer.Reset();


timer.Start();

for (int i = 0; i < 30000; i++)[/quote]> result = testString.Split(new[] { [quote]Environment.NewLine }, StringSplitOptions.None);

timer.Stop();

TimeSpan stringSplitTime = timer.Elapsed;


Console.WriteLine("Regex: {0}", regexTime.TotalMilliseconds);
Console.WriteLine("StrSplit: {0}", stringSplitTime.TotalMilliseconds);
Console.ReadLine();
}


I mam nieznaczną przewagę regexa :)

Jeśli już tak tuningujemy to dzielmy po charach a nie po stringach i będzie minimalna różnica po stronie str.split :P hehe (na tyle minimalna, że można ją lekceważyć)

To co chciałem podkreślić w mojej pierwszej odpowiedzi, to jest to, że bardzo często ludzie używają narzędzi które są niepotrzebnie za ciężkie / nieoptymalne do danego zadania.

Osobiście, jakbym musiał wykonać identycznie takie dzielenie stringów jak w tym temacie to bym nie użył ani jednej ani drugiej metody, tylko po prostu jedną pętlę for która zrobi to kilka razy szybciej a wysiłek ten sam.

Tak jako ciekawostka:


static void Main(string[] args)
{
Stopwatch timer = new Stopwatch();
string testString = File.ReadAllText(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\text.txt");

string[] result;


timer.Start();

Regex regex = new Regex(@"\r\n", RegexOptions.Compiled);
for (int i = 0; i < 30000; i++)
result = regex.Split(testString);

timer.Stop();

TimeSpan regexTime = timer.Elapsed;
timer.Reset();


timer.Start();

for (int i = 0; i < 30000; i++)
result = testString.Split(new[] { '\r','\n' }, StringSplitOptions.RemoveEmptyEntries);

timer.Stop();
TimeSpan stringSplitTime = timer.Elapsed;

List<String> lines = new List<string>();

timer.Reset();
timer.Start();

for (int i = 0, lastPos = 0; i < 3000; i++, lastPos = 0)
{
for (int j = 0; j < testString.Length - 1; j++)
{
if (testString[j] == '\r' && testString[j + 1] == '\n')
{
lines.Add(testString.Substring(lastPos, j - lastPos));
j += 2;
lastPos = j;
}
}
lines.Add(testString.Substring(lastPos, testString.Length - lastPos));
}
TimeSpan manualTime = timer.Elapsed;


Console.WriteLine("Regex: {0}", regexTime.TotalMilliseconds);
Console.WriteLine("StrSplit: {0}", stringSplitTime.TotalMilliseconds);
Console.WriteLine("Manual: {0}", manualTime.TotalMilliseconds);
Console.ReadLine();
}


Wynik:

Regex: 791,965
StrSplit: 771,9464
Manual: 118,0244

konto usunięte

Temat: Czy znacie taką bibliotekę, którą zamienie mi...

Karim A.:

Osobiście, jakbym musiał wykonać identycznie takie dzielenie stringów jak w tym temacie to bym nie użył ani jednej ani drugiej metody, tylko po prostu jedną pętlę for która zrobi to kilka razy szybciej a wysiłek ten sam.

Ja bym skorzystał ze Split, a w czasie gdy Ty pisałbyś i testował pętle uciąłbym sobie drzemkę. Z jednej strony piszesz o "za ciężkich narzędziach", z drugiej chcesz wymyślać koło na nowo :)

EDIT: To byłaby turbo-drzemka.Jarosław D. edytował(a) ten post dnia 20.08.10 o godzinie 13:39

Następna dyskusja:

WPF - czy już stosujecie?




Wyślij zaproszenie do