Zwei Linked Lists vergleichen

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
Makke
Beiträge: 156
Registriert: 24.08.2011 18:00
Computerausstattung: AMD Ryzen 7 5700X - AMD Radeon RX 6800 XT - 32 GB DDR4 SDRAM
Wohnort: Ruhrpott
Kontaktdaten:

Zwei Linked Lists vergleichen

Beitrag von Makke »

Hallo,

zur Übung schreibe ich gerade ein Programm das zwei Verzeichnisse miteinander vergleicht, um dann die fehlenden Dateien zu kopieren. Die Verzeichnisse lese ich in je eine separate strukturierte LinkedList ein.

Die Listen werden dann verglichen, was bei mir relativ lange dauert, hier der Code:

Code: Alles auswählen

Structure DirectoryEntryDescriptor
  path.s
  name.s
  size.i
  type.s
  isdir.b
  issystem.b
  ishidden.b
EndStructure
Procedure.i CompareLinkedLists(List Source.DirectoryEntryDescriptor(), SourceRootPath.s, List Dest.DirectoryEntryDescriptor(), DestinationRootPath.s, List Result.DirectoryEntryDescriptor())
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  Define FoundEntry.b = #False
  If ListSize(Source()) = 0
    ProcedureReturn -1
  EndIf
  If ListSize(Dest()) = 0
    CopyList(Source(), Result())
    ProcedureReturn ListSize(Result())
  EndIf
  If Right(SourceRootPath, 1) <> "\"
    SourceRootPath = SourceRootPath + "\"
  EndIf
  If Right(DestinationRootPath, 1) <> "\"
    DestinationRootPath = DestinationRootPath + "\"
  EndIf
  ForEach Source()
    If Source()\issystem And OptionIncSysfiles = #False
      Continue
    EndIf
    If Source()\ishidden And OptionIncHidfiles = #False
      Continue
    EndIf
    ForEach Dest()
      If ReplaceString(Source()\path + Source()\name, SourceRootPath, "") = ReplaceString(Dest()\path + Dest()\name, DestinationRootPath, "")
        FoundEntry = #True
        Break
      EndIf
    Next
    If FoundEntry = #False
      AddElement(Result())
      Result() = Source()
    Else
      FoundEntry = #False
    EndIf
  Next
  ProcedureReturn ListSize(Result())
EndProcedure
Meine Frage ist, ob jemand schon mal eine schnellere Lösung dafür gefunden hat ?
Zuletzt geändert von Makke am 19.09.2011 13:01, insgesamt 1-mal geändert.
---
Windows 11 (64 bit)
c4s
Beiträge: 1235
Registriert: 19.09.2007 22:18

Re: Zwei Linked Lists vergleichen

Beitrag von c4s »

Ein Anfang wäre nicht ReplaceString() zu verwenden. Der Sinn davon erschließt sich mir auch noch nicht ganz.

Der Einfachheit halber könntest du außerdem \ishidden bzw. \issystem in bspw. \attributes als Flag abspeichern (\attributes = GetFileAttributes()).
"Menschenskinder, das Niveau dieses Forums singt schon wieder!" — GronkhLP ||| "ich hogffe ihr könnt den fehle endecken" — Marvin133 ||| "Ideoten gibts ..." — computerfreak ||| "Jup, danke. Gruss" — funkheld
Christian+
Beiträge: 213
Registriert: 13.07.2008 10:05
Computerausstattung: Windows 8.1 Pro
AMD Phenom II X4 955 @ 3.2 GHz
4GB RAM
NVIDIA GeForce GTX 660

Re: Zwei Linked Lists vergleichen

Beitrag von Christian+ »

Also du verlierst auf jeden Fall viel Geschwindigkeit durch die vielen ReplaceString aufrufe.
Es sollte auf jeden Fall schneller sein wenn du erst nur die Dateinamen vergleichst.
Sollten diese gleich sein schaust du dann erst ob der Dateipfad auch gleich ist.
Dabei am besten den Source Teil vor der Schleife ermitteln damit das nicht jedes Mal gemacht wird so sparst du dabei dann auch nochmal Zeit.
So könnte es z.B. dann aussehen (nur zu Veranschaulichung ich habe den Code nicht getestet):

Code: Alles auswählen

SourcePath = ReplaceString(Source()\path, SourceRootPath, "") 
ForEach Dest()
  If Source()\name = Dest()\name
    If SourcePath = ReplaceString(Dest()\path, DestinationRootPath, "")
      FoundEntry = #True
      Break
    EndIf
  EndIf
Next
Windows 8.1 Pro 64Bit | AMD Phenom II X4 955 @ 3.2 GHz | 4GB RAM | NVIDIA GeForce GTX 660
Benutzeravatar
Makke
Beiträge: 156
Registriert: 24.08.2011 18:00
Computerausstattung: AMD Ryzen 7 5700X - AMD Radeon RX 6800 XT - 32 GB DDR4 SDRAM
Wohnort: Ruhrpott
Kontaktdaten:

Re: Zwei Linked Lists vergleichen

Beitrag von Makke »

c4s hat geschrieben:Ein Anfang wäre nicht ReplaceString() zu verwenden. Der Sinn davon erschließt sich mir auch noch nicht ganz.
...
In den strukturierten Listen wird der komplette Pfad gespeichert. Bsp: C:\music\Independent\Sisters of Mercy\ wobei der ursprüngliche Suchpfad in dem Beispiel dann C:\music ist. Um die relativen Pfade dann zu vergleichen müssen die beiden Suchpfade natürlich entfernt werden.

Irgendwann muss ich diesen Vergleich anstellen, aber vielleicht bringt es schon etwas wenn ich das vorher mache und nicht in dieser Schleife. Ich werde das mal testen. Danke schonmal.
---
Windows 11 (64 bit)
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Zwei Linked Lists vergleichen

Beitrag von STARGÅTE »

Wenn der RootPfad eh immer der selbe ist für eine Liste,
warum speicherst du denn dann nicht gleich nur die relativen Pfade in der LinkedList?

ReplaceString() sollte auf jedenfall komplett vermieden werden, innerhalb einer Doppelschleife, auch wenn Christian+ es schon etwas eleganter gemacht hat.

Außerdem kannst du doch alle die Einträge die bereich erfolgreich verglichen wurden (das sie in beiden Listen existieren) auf der einen Liste löschen.
Somit wird nicht immer wieder erneut mit diesem Element verglichen, denn es wird ja eh keine erneute Gleichheit auftreten.
Dann würde die Laufzeit zum ca. 50% sinken
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8808
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Zwei Linked Lists vergleichen

Beitrag von NicTheQuick »

Wenn du die Listen vorher sortierst kannst du das ganze statt in Laufzeit O(m * n) auch in Laufzeit O(m * log(m) + n * log(n)) machen, was wesentlich schneller ist.

Ich versuche mal ein Beispiel zu basteln.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8808
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Zwei Linked Lists vergleichen

Beitrag von NicTheQuick »

Dann also hier mal ein kleiner Doppelpost:

Code: Alles auswählen

Structure DirectoryEntryDescriptor
	path.s			;relativer Pfad zur Datei ab root
	size.i			;Dateigröße
	type.s			;Dateityp
	isDir.i			;#True, wenn dies ein Ordner ist
	isSystem.i		;#True, wenn dies eine Systemdatei ist
	isHidden.i		;#True, wenn dies eine versteckte Datei ist
EndStructure

Structure Directory
	root.s			;Wurzelverzeichnis
	List content.DirectoryEntryDescriptor()
EndStructure

Procedure readDirectory(dir.s, *out.Directory, subDir.s = "")
	Protected hDir.i, name.s
	
	If (Not *out)
		ProcedureReturn #False
	EndIf
	If (subDir = "")
		ClearList(*out\content())
		*out\root = dir
	EndIf
	
	CompilerIf (#PB_Compiler_OS = #PB_OS_Windows)
	If (Right(dir, 1) <> "\")
	CompilerElse
	If (Right(dir, 1) <> "/")
	CompilerEndIf
		dir + "/"
	EndIf
	
	hDir = ExamineDirectory(#PB_Any, dir + subDir, "")
	If (Not hDir)
		ProcedureReturn #False
	EndIf
	
	While (NextDirectoryEntry(hDir))
		name = DirectoryEntryName(hDir)
		If (name = "." Or name = "..")
			Continue
		EndIf
		If (AddElement(*out\content()))
			With *out\content()
				\path = subDir + name
				CompilerSelect #PB_Compiler_OS
					CompilerCase #PB_OS_Windows
						If (DirectoryEntryType(hDir) & #PB_FileSystem_Hidden)
							\isHidden = #True
						EndIf
						If (DirectoryEntryType(hDir) & #PB_FileSystem_System)
							\isSystem = #True
						EndIf
					CompilerCase #PB_OS_Linux
						\isSystem = #False
						If (Left(name, 1) = ".")
							\isHidden = #True
						EndIf
					CompilerDefault
						\isSystem = #False
						\isHidden = #False
				CompilerEndSelect
				
				\type = GetExtensionPart(name)
				
				If (DirectoryEntryType(hDir) = #PB_DirectoryEntry_Directory)
					\size = 0
					\isDir = #True
					CompilerIf (#PB_Compiler_OS	= #PB_OS_Windows)
						readDirectory(dir, *out, name + "\")
					CompilerElse
						readDirectory(dir, *out, name + "/")
					CompilerEndIf
				Else
					\size = DirectoryEntrySize(hDir)
					\isDir = #False
				EndIf
			EndWith
		EndIf
	Wend
	
	FinishDirectory(hDir)
	
	ProcedureReturn *out
EndProcedure

Procedure compareDirectories(*source.Directory, *dest.Directory, *diff.Directory)
	Protected destEmpty.i = #False
	
	CompilerIf (#PB_Compiler_OS = #PB_OS_Windows)
		#SortFlags = #PB_Sort_Ascending | #PB_Sort_NoCase
		#CompareFlags = #PB_String_NoCase
	CompilerElse
		#SortFlags = #PB_Sort_Ascending
		#CompareFlags = 0
	CompilerEndIf
	
	SortStructuredList(*source\content(), #SortFlags, OffsetOf(DirectoryEntryDescriptor\path), #PB_Sort_String)
	SortStructuredList(*dest\content(), #SortFlags, OffsetOf(DirectoryEntryDescriptor\path), #PB_Sort_String)
	
	ClearList(*diff\content())
	*diff\root = *source\root
	
	ResetList(*dest\content())
	ResetList(*source\content())
	If (NextElement(*dest\content()))
		If (NextElement(*source\content()))
			Repeat
				If (Not destEmpty)
					Select CompareMemoryString(@*source\content()\path, @*dest\content()\path, #CompareFlags)
						Case  #PB_String_Equal
							If (Not NextElement(*dest\content()))
								destEmpty = #True
							EndIf
							If (Not NextElement(*source\content()))
								Break
							EndIf
						Case #PB_String_Lower
							If (AddElement(*diff\content()))
								*diff\content() = *source\content()
							EndIf
							If (Not NextElement(*source\content()))
								Break
							EndIf
						Case #PB_String_Greater
							If (Not NextElement(*dest\content()))
								destEmpty = #True
							EndIf
					EndSelect
				Else
					If (AddElement(*diff\content()))
						*diff\content() = *source\content()
					EndIf
				EndIf
			ForEver
		EndIf
	Else
		CopyStructure(*source, *diff, Directory)
	EndIf
	
	ProcedureReturn *diff
EndProcedure

Define source.Directory, dest.Directory, diff.Directory

readDirectory("/home/nicolas/tmp/XMasRun2", source)
readDirectory("/home/nicolas/tmp/XMasRun2b", dest)

compareDirectories(source, dest, diff)

Debug "Dateien, die in source fehlen:"
ForEach diff\content()
	Debug diff\content()\path
Next
Ich hoffe das ist alles so verständlich.
Benutzeravatar
Makke
Beiträge: 156
Registriert: 24.08.2011 18:00
Computerausstattung: AMD Ryzen 7 5700X - AMD Radeon RX 6800 XT - 32 GB DDR4 SDRAM
Wohnort: Ruhrpott
Kontaktdaten:

Re: Zwei Linked Lists vergleichen

Beitrag von Makke »

Erstmal vielen Dank an alle für die Tips.

Auf einfachem Wege habe ich in der Vergleichsschleife das Replace-String entfernt. Der Erfolg, bei Listen mit ca. 3500 Einträgen ist die Zeit von 10 auf 3 Sekunden geschrumpft. Das ist schonmal spitze.

@Stargate: Deinen Vorschlag die verglichenen EInträge zu entfernen ist ebenfalls prima, den habe ich eingebaut und die 3 Sekunden auf 1,8 Sekunden nochmals reduziert. Vielen Dank.

@NickTheQuick: Danke für Dein Beispiel, das werde ich mal genauer unter die Lupe nehmen. Aber eine Frage habe ich, warum arbeitest Du bei der Prozedurübergabe mit Zeigern und übergibst die Liste nicht direkt. Persönliche Vorliebe oder birgt das irgendwelche Vorteile ?

Nochmal zu dem Progrämmchen, ursprünglich hatte ich nur die Verzeichnis-Einlese-Prozedur und die Vergleichs-Prozedur, das ganze hab ich mir dann mit Debug ausgeben lassen, jetzt habe ich ein Fenster drum herumgebaut, hier das Ergebnis:

Code: Alles auswählen

; Synchronizer
; synch two directories including their subdirs
; (c) 2011 by Makke
EnableExplicit
;- Constants
;-- App
#APPNAME = "SyncIt"
#APPMAJOR = 0
#APPMINOR = 1
;-- Window
Enumeration
  #mainWnd
  #toolWnd
EndEnumeration
;-- Gadgets
Enumeration
  #mainStatusbar
  #mainProgressbar
  #mainFrame_Source
  #mainFrame_Dest
  #mainFrame_Opt
  #mainFrame_Sync
  #mainBtn_DirSource
  #mainBtn_DirDest
  #mainBtn_Check
  #mainBtn_ShowDiff
  #mainBtn_Sync
  #mainChk_incDirs
  #mainChk_incSubdirs
  #mainChk_incSystem
  #mainChk_incHidden
  #mainTxt_SourceDir
  #mainTxt_SourceInfo
  #mainTxt_SourceFiles
  #mainTxt_SourceFilesNo
  #mainTxt_SourceDirs
  #mainTxt_SourceDirsNo
  #mainTxt_DestDir
  #mainTxt_DestInfo
  #mainTxt_DestFiles
  #mainTxt_DestFilesNo
  #mainTxt_DestDirs
  #mainTxt_DestDirsNo
  #mainTxt_Sync
  #toolLst_Diff
EndEnumeration

;- Structures
Structure DirectoryEntryDescriptor
  path.s
  relativepath.s
  name.s
  size.i
  type.s
  isdir.b
  issystem.b
  ishidden.b
EndStructure

;- Structured Lists
NewList SourceFileList.DirectoryEntryDescriptor()
NewList DestFileList.DirectoryEntryDescriptor()
NewList DiffFileList.DirectoryEntryDescriptor()

;- Variables
;-- Windows
Define WindowEvent.i
Define WIndowID.i
Define GadgetID.i
Define QUIT.b
;-- Settings
Define SourceDir.s
Define DestDir.s
Define OptionIncDirs.b
Define OptionIncSubdirs.b
Define OptionIncSysfiles.b
Define OptionIncHidfiles.b

;- Procedures
;-- Declarations
Declare.i GetDirectoryContent(List FileList.DirectoryEntryDescriptor(), FullPath.s, IncludeSubdirs.b = #False, AddDirEntryToList.b = #False)
Declare.i CountFilesInList(List ListOfFiles.DirectoryEntryDescriptor())
Declare.i CountDirsInList(List ListOfDirs.DirectoryEntryDescriptor())
Declare.i CompareLinkedLists(List Source.DirectoryEntryDescriptor(), SourceRootPath.s, List Dest.DirectoryEntryDescriptor(), DestinationRootPath.s, List Result.DirectoryEntryDescriptor())
Declare SelectOptions()
;-- Helper
Procedure.s StrB(BoolVariable.b)
  If BoolVariable = #True
    ProcedureReturn "True"
  Else
    ProcedureReturn "False"
  EndIf
EndProcedure
Procedure.b ValB(BoolExpression.s)
  If UCase(BoolExpression) = "TRUE"
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure
Procedure FinishList(List FileList.DirectoryEntryDescriptor(), FileListRootpath.s)
  If Right(FileListRootpath, 1) <> "\"
    FileListRootpath = FileListRootpath + "\"
  EndIf
  ForEach FileList()
    With FileList()
      \relativepath = ReplaceString(\path, FileListRootpath, "")
    EndWith
  Next
EndProcedure
;-- Settings
Procedure LoadSettings()
  Shared SourceDir
  Shared DestDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  If OpenPreferences(#APPNAME + ".conf")
    SourceDir         = ReadPreferenceString("SourceDirectory", "")
    DestDir           = ReadPreferenceString("DestinationDirectory", "")
    OptionIncDirs     = ValB(ReadPreferenceString("IncludeDirectories", "True"))
    OptionIncSubdirs  = ValB(ReadPreferenceString("IncludeSubdirectories", "False"))
    OptionIncSysfiles = ValB(ReadPreferenceString("IncludeSystemfiles", "False"))
    OptionIncHidfiles = ValB(ReadPreferenceString("IncludeHiddenfiles", "False"))
    ClosePreferences()
  EndIf
EndProcedure
Procedure SaveSettings()
  Shared SourceDir
  Shared DestDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  If CreatePreferences(#APPNAME + ".conf")
    PreferenceComment(#APPNAME + " v" + Str(#APPMAJOR) + "." + Str(#APPMINOR) + "." + Str(#PB_Editor_BuildCount) + " config file")
    WritePreferenceString("SourceDirectory", SourceDir)
    WritePreferenceString("DestinationDirectory", DestDir)
    WritePreferenceString("IncludeDirectories", StrB(OptionIncDirs))
    WritePreferenceString("IncludeSubdirectories", StrB(OptionIncSubdirs))
    WritePreferenceString("IncludeSystemfiles", StrB(OptionIncSysfiles))
    WritePreferenceString("IncludeHiddenfiles", StrB(OptionIncHidfiles))
    ClosePreferences()
  EndIf
EndProcedure
Procedure.b OpenSourceDir()
  Shared SourceFileList()
  Shared SourceDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Define time.i
  If SourceDir = ""
    ProcedureReturn #False
  EndIf
  If ListSize(SourceFileList()) > 0
    ClearList(SourceFileList())
  EndIf
  SetGadgetText(#mainTxt_SourceDir, SourceDir)
  StatusBarText(#mainStatusbar, 0, "Scanning source directory ...")
  time = ElapsedMilliseconds()
  GetDirectoryContent(SourceFileList(), SourceDir, OptionIncSubdirs, OptionIncDirs)
  SetGadgetState(#mainProgressbar, 50)
  SetGadgetText(#mainTxt_SourceFilesNo, Str(CountFilesInList(SourceFileList())))
  SetGadgetText(#mainTxt_SourceDirsNo, Str(CountDirsInList(SourceFileList())))
  FinishList(SourceFileList(), SourceDir)
  time = ElapsedMilliseconds() - time
  SetGadgetState(#mainProgressbar, 100)
  StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  ProcedureReturn #True
EndProcedure
Procedure.b OpenDestDir()
  Shared DestFileList()
  Shared DestDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Define time.i
  If DestDir = ""
    ProcedureReturn #False
  EndIf
  If ListSize(DestFileList())
    ClearList(DestFileList())
  EndIf
  SetGadgetText(#mainTxt_DestDir, DestDir)
  StatusBarText(#mainStatusbar, 0, "Scanning destination directory ...")
  time = ElapsedMilliseconds()
  GetDirectoryContent(DestFileList(), DestDir, OptionIncSubdirs, OptionIncDirs)
  SetGadgetState(#mainProgressbar, 50)
  SetGadgetText(#mainTxt_DestFilesNo, Str(CountFilesInList(DestFileList())))
  SetGadgetText(#mainTxt_DestDirsNo, Str(CountDirsInList(DestFileList())))
  FinishList(DestFileList(), DestDir)
  time = ElapsedMilliseconds() - time
  SetGadgetState(#mainProgressbar, 100)
  StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  ProcedureReturn #True
EndProcedure
;-- Directory
Procedure.i GetDirectoryContent(List FileList.DirectoryEntryDescriptor(), FullPath.s, IncludeSubdirs.b = #False, AddDirEntryToList.b = #False)
  Define dirId.i
  Define entrytype.i
  If Right(FullPath, 1) <> "\"
    FullPath = FullPath + "\"
  EndIf
  dirId = ExamineDirectory(#PB_Any, FullPath, "*.*")
  If IsDirectory(dirId)
    While NextDirectoryEntry(dirId)
      entrytype = DirectoryEntryType(dirID)
      If entrytype = #PB_DirectoryEntry_File
        AddElement(FileList())
        With FileList()
          \path  = FullPath
          \name  = DirectoryEntryName(dirId)
          \size  = DirectoryEntrySize(dirId)
          \type  = GetExtensionPart(\name)
          \isdir = #False
          If DirectoryEntryAttributes(dirId) & #PB_FileSystem_System
            \issystem = #True
          EndIf
          If DirectoryEntryAttributes(dirId) & #PB_FileSystem_Hidden
            \ishidden = #True
          EndIf
        EndWith
      ElseIf entrytype = #PB_DirectoryEntry_Directory
        If AddDirEntryToList
          If DirectoryEntryName(dirId) <> "." And DirectoryEntryName(dirId) <> ".."
            AddElement(FileList())
            With FileList()
              \path  = FullPath
              \name  = DirectoryEntryName(dirId)
              \size  = 0
              \type  = "DIR"
              \isdir = #True
              If DirectoryEntryAttributes(dirId) & #PB_FileSystem_System
                \issystem = #True
              EndIf
              If DirectoryEntryAttributes(dirId) & #PB_FileSystem_Hidden
                \ishidden = #True
              EndIf
            EndWith
          EndIf
        EndIf
        If IncludeSubdirs
          If DirectoryEntryName(dirId) <> "." And DirectoryEntryName(dirId) <> ".."
            GetDirectoryContent(FileList(), FullPath + DirectoryEntryName(dirId) + "\", #True, AddDirEntryToList)
          EndIf
        EndIf
      EndIf
    Wend
    FinishDirectory(dirID)
  EndIf
  ProcedureReturn ListSize(FileList())
EndProcedure
Procedure.i CompareLinkedLists(List Source.DirectoryEntryDescriptor(), SourceRootPath.s, List Dest.DirectoryEntryDescriptor(), DestinationRootPath.s, List Result.DirectoryEntryDescriptor())
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  Define FoundEntry.b = #False
  Define size.f
  If ListSize(Source()) = 0
    ProcedureReturn -1
  EndIf
  If ListSize(Result()) > 0
    ClearList(Result())
  EndIf
  size = ListSize(Source()) / 100
  If ListSize(Dest()) = 0
    CopyList(Source(), Result())
    ProcedureReturn ListSize(Result())
  EndIf
  If Right(SourceRootPath, 1) <> "\"
    SourceRootPath = SourceRootPath + "\"
  EndIf
  If Right(DestinationRootPath, 1) <> "\"
    DestinationRootPath = DestinationRootPath + "\"
  EndIf
  ForEach Source()
    If Source()\issystem And OptionIncSysfiles = #False
      Continue
    EndIf
    If Source()\ishidden And OptionIncHidfiles = #False
      Continue
    EndIf
    ForEach Dest()
      If Source()\relativepath + Source()\name = Dest()\relativepath + Dest()\name
        DeleteElement(Dest())
        FoundEntry = #True
        Break
      EndIf
    Next
    If FoundEntry = #False
      AddElement(Result())
      Result() = Source()
    Else
      FoundEntry = #False
    EndIf
    If IsGadget(#mainProgressbar)
      SetGadgetState(#mainProgressbar, Round(ListIndex(Source()) / size, #PB_Round_Nearest))
    EndIf
  Next
  ProcedureReturn ListSize(Result())
EndProcedure
Procedure.i CountFilesInList(List ListOfFiles.DirectoryEntryDescriptor())
  Define NoOfFiles.i
  If ListSize(ListOfFiles()) = 0
    ProcedureReturn 0
  EndIf
  ForEach ListOfFiles()
    If ListOfFiles()\isdir = #False
      NoOfFiles = NoOfFiles + 1
    EndIf
  Next
  ProcedureReturn NoOfFiles
EndProcedure
Procedure.i CountDirsInList(List ListOfDirs.DirectoryEntryDescriptor())
  Define NoOfDirs.i
  If ListSize(ListOfDirs()) = 0
    ProcedureReturn 0
  EndIf
  ForEach ListOfDirs()
    If ListOfDirs()\isdir = #True
      NoOfDirs = NoOfDirs + 1
    EndIf
  Next
  ProcedureReturn NoOfDirs
EndProcedure
Procedure.i CountAllEntriesInList(List ListToCount.DirectoryEntryDescriptor())
  ProcedureReturn ListSize(ListToCount())
EndProcedure
Procedure CheckDestinationPath(SourcePath.s)
  Shared SourceDir
  Shared DestDir
  Define pathpart.s = ReplaceString(SourcePath, SourceDir, "")
  Define subdirs.i  = CountString(pathpart, "\")
  Define i.i
  Define buildpath.s
  Define dir.i
  If Right(DestDir, 1) <> "\"
    DestDir = DestDir + "\"
  EndIf
  buildpath = DestDir
  For i = 1 To subdirs
    buildpath = buildpath + StringField(pathpart, i, "\") + "\"
    dir = ExamineDirectory(#PB_Any, buildpath, "*")
    If IsDirectory(dir)
      FinishDirectory(dir)
    Else
      CreateDirectory(buildpath)
    EndIf
  Next
EndProcedure
;-- Window
Procedure OpenMainWindow()
  Define FontID1.i
  Define FontID2.i
  ; create window
  If OpenWindow(#mainWnd, 438, 258, 600, 300, #APPNAME,  #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_MinimizeGadget | #PB_Window_Invisible)
    ; load fonts and set windows default font
    FontID1 = LoadFont(1, "Segoe UI", 8)
    FontID2 = LoadFont(2, "Segoe UI", 10)
    SetGadgetFont(#PB_Default, FontID1)
    ; create statusbar
    If CreateStatusBar(#mainStatusbar, WindowID(#mainWnd))
      AddStatusBarField(400)
      AddStatusBarField(200)
      StatusBarText(#mainStatusbar, 0, "Select a source and destination directory ...")
      StatusBarText(#mainStatusbar, 1, "")
    EndIf
    ; create gadgets
    ProgressBarGadget(#mainProgressbar, 405, 282, 190, 15, 0, 100, #PB_ProgressBar_Smooth)
    ; source frame content
    Frame3DGadget(#mainFrame_Source, 5, 10, 290, 125, "Source Directory:") : SetGadgetFont(#mainFrame_Source, FontID2)
    TextGadget(#mainTxt_SourceDir, 10, 30, 260, 20, "", #PB_Text_Border)
    ButtonGadget(#mainBtn_DirSource, 270, 30, 20, 20, "...")
    TextGadget(#mainTxt_SourceInfo, 10, 55, 280, 20, "Information:")
    TextGadget(#mainTxt_SourceFiles, 10, 80, 100, 20, "No. of Files:")
    TextGadget(#mainTxt_SourceFilesNo, 115, 80, 175, 20, "")
    TextGadget(#mainTxt_SourceDirs, 10, 105, 100, 20, "No. of Directories:")
    TextGadget(#mainTxt_SourceDirsNo, 115, 105, 175, 20, "")
    ; destination frame content
    Frame3DGadget(#mainFrame_Dest, 305, 10, 290, 125, "Destination Directory:") : SetGadgetFont(#mainFrame_Dest, FontID2)
    TextGadget(#mainTxt_DestDir, 310, 30, 260, 20, "", #PB_Text_Border)
    ButtonGadget(#mainBtn_DirDest, 570, 30, 20, 20, "...")
    TextGadget(#mainTxt_DestInfo, 310, 55, 280, 20, "Information:")
    TextGadget(#mainTxt_DestFiles, 310, 80, 100, 20, "No. of Files:")
    TextGadget(#mainTxt_DestFilesNo, 415, 80, 175, 20, "")
    TextGadget(#mainTxt_DestDirs, 310, 105, 100, 20, "No. of Directories:")
    TextGadget(#mainTxt_DestDirsNo, 415, 105, 175, 20, "")
    ; filter frame content
    Frame3DGadget(#mainFrame_Opt, 5, 145, 590, 55, "Filter Options:") : SetGadgetFont(#mainFrame_Opt, FontID2)
    CheckBoxGadget(#mainChk_incDirs, 20, 170, 135, 20, "include directories")
    CheckBoxGadget(#mainChk_incSubdirs, 160, 170, 135, 20, "include sub directories")
    CheckBoxGadget(#mainChk_incSystem, 310, 170, 135, 20, "include system files")
    CheckBoxGadget(#mainChk_incHidden, 455, 170, 135, 20, "include hidden files")
    ; sync frame content
    Frame3DGadget(#mainFrame_Sync, 5, 210, 590, 60, "Synchronize:") : SetGadgetFont(#mainFrame_Sync, FontID2)
    ButtonGadget(#mainBtn_Check, 10, 235, 125, 20, "Check Directories")
    TextGadget(#mainTxt_Sync, 140, 225, 320, 20, "", #PB_Text_Center)
    ButtonGadget(#mainBtn_ShowDiff, 238, 245, 125, 20, "Show Differences") : DisableGadget(#mainBtn_ShowDiff, #True)
    ButtonGadget(#mainBtn_Sync, 465, 235, 125, 20, "Copy files") : DisableGadget(#mainBtn_Sync, #True)
    ; set checkboxes
    SelectOptions()
    ; show window
    HideWindow(#mainWnd, #False)
  EndIf
EndProcedure
Procedure OpenDifferenceWindow()
  If OpenWindow(#toolWnd, WindowX(#mainWnd), WindowY(#mainWnd) + 40, WindowWidth(#mainWnd), 200, "", #PB_Window_Tool | #PB_Window_SystemMenu, WindowID(#mainWnd))
    ListViewGadget(#toolLst_Diff, 0, 0, WindowWidth(#toolWnd), WindowHeight(#toolWnd))
  EndIf
EndProcedure
;-- Gadget Events
Procedure SelectOptions()
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Shared OptionIncSysfiles
  Shared OptionIncHidfiles
  If OptionIncDirs
    SetGadgetState(#mainChk_incDirs, #True)
  EndIf
  If OptionIncSubdirs
    OptionIncDirs = #True
    SetGadgetState(#mainChk_incDirs, #True)
    SetGadgetState(#mainChk_incSubdirs, #True)
  EndIf
  If OptionIncSysfiles
    SetGadgetState(#mainChk_incSystem, #True)
  EndIf
  If OptionIncHidfiles
    SetGadgetState(#mainChk_incHidden, #True)
  EndIf
EndProcedure
Procedure SelectSourceDir()
  Shared SourceFileList()
  Shared SourceDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Define time.i
  SourceDir = PathRequester("Select source directory", ".")
  If SourceDir <> ""
    If ListSize(SourceFileList()) > 0
      ClearList(SourceFileList())
    EndIf
    SetGadgetText(#mainTxt_SourceDir, SourceDir)
    StatusBarText(#mainStatusbar, 0, "Scanning source directory ...")
    time = ElapsedMilliseconds()
    GetDirectoryContent(SourceFileList(), SourceDir, OptionIncSubdirs, OptionIncDirs)
    SetGadgetText(#mainTxt_SourceFilesNo, Str(CountFilesInList(SourceFileList())))
    SetGadgetText(#mainTxt_SourceDirsNo, Str(CountDirsInList(SourceFileList())))
    FinishList(SourceFileList(), SourceDir)
    time = ElapsedMilliseconds() - time
    SetGadgetState(#mainProgressbar, 100)
    StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  EndIf
EndProcedure
Procedure SelectDestDir()
  Shared DestFileList()
  Shared DestDir
  Shared OptionIncDirs
  Shared OptionIncSubdirs
  Define time.i
  DestDir = PathRequester("Select destination directory", ".")
  If DestDir <> ""
    If ListSize(DestFileList())
      ClearList(DestFileList())
    EndIf
    SetGadgetText(#mainTxt_DestDir, DestDir)
    StatusBarText(#mainStatusbar, 0, "Scanning destination directory ...")
    time = ElapsedMilliseconds()
    GetDirectoryContent(DestFileList(), DestDir, OptionIncSubdirs, OptionIncDirs)
    SetGadgetState(#mainProgressbar, 50)
    SetGadgetText(#mainTxt_DestFilesNo, Str(CountFilesInList(DestFileList())))
    SetGadgetText(#mainTxt_DestDirsNo, Str(CountDirsInList(DestFileList())))
    FinishList(DestFileList(), DestDir)
    time = ElapsedMilliseconds() - time
    SetGadgetState(#mainProgressbar, 100)
    StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  EndIf
EndProcedure
Procedure SelectCheckButton()
  Shared SourceFileList()
  Shared SourceDir
  Shared DestFileList()
  Shared DestDir
  Shared DiffFileList()
  Define time.i
  StatusBarText(#mainStatusbar, 0, "Checking directories for differences ...")
  time = ElapsedMilliseconds()
  CompareLinkedLists(SourceFileList(), SourceDir, DestFileList(), DestDir, DiffFileList())
  time = ElapsedMilliseconds() - time
  SetGadgetState(#mainProgressbar, 100)
  StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  If ListSize(DiffFileList()) > 0
    SetGadgetText(#mainTxt_Sync, Str(ListSize(DiffFileList())) + " files and dirs are missing in destination directory")
    DisableGadget(#mainBtn_ShowDiff, #False)
    DisableGadget(#mainBtn_Sync, #False)
  Else
    SetGadgetText(#mainTxt_Sync, "No files or dirs are missing in destination directory")
  EndIf
EndProcedure
Procedure SelectDiffButton()
  Shared DiffFileList()
  If IsWindow(#toolWnd)
    CloseWindow(#toolWnd)
    SetGadgetText(#mainBtn_ShowDiff, "Show Differences")
  Else
    OpenDifferenceWindow()
    ForEach DiffFileList()
      AddGadgetItem(#toolLst_Diff, -1, DiffFileList()\path + DiffFileList()\name)
    Next
    SetGadgetText(#mainBtn_ShowDiff, "Hide Differences")
  EndIf
EndProcedure
Procedure.b SelectSyncButton()
  Shared DiffFileList()
  Shared SourceDir
  Shared DestDir
  NewList errorlist.DirectoryEntryDescriptor()
  Define size.f
  Define time.i
  If ListSize(DiffFileList()) = 0
    ProcedureReturn #False
  EndIf
  If Right(SourceDir, 1) <> "\"
    SourceDir = SourceDir + "\"
  EndIf
  If Right(DestDir, 1) <> "\"
    DestDir = DestDir + "\"
  EndIf
  size = ListSize(DiffFileList()) / 100
  StatusBarText(#mainStatusbar, 0, "Copy missing source files to destination ...")
  time.i = ElapsedMilliseconds()
  ForEach DiffFileList()
    If DiffFileList()\isdir
      Continue
    EndIf
    CheckDestinationPath(DiffFileList()\path)
    If Not CopyFile(DiffFileList()\path + DiffFileList()\name, ReplaceString(DiffFileList()\path, SourceDir, DestDir) + DiffFileList()\name)
      AddElement(errorlist())
      errorlist()\path = DiffFileList()\path
      errorlist()\name = DiffFileList()\name
    EndIf
    SetGadgetState(#mainProgressbar, Round(ListIndex(DiffFileList()) / size, #PB_Round_Nearest))
  Next
  time = ElapsedMilliseconds() - time
  StatusBarText(#mainStatusbar, 0, "Done, took " + Str(time) + " miliseconds.")
  ClearList(DiffFileList())
  CopyList(errorlist(), DiffFileList())
  ClearList(errorlist())
  If ListSize(DiffFileList()) > 0 
    SetGadgetText(#mainTxt_Sync, "There are errors occured during the copy process.")
    SetGadgetText(#mainBtn_ShowDiff, "Show Errors")
  Else
    SetGadgetText(#mainTxt_Sync, "")
    DisableGadget(#mainBtn_ShowDiff, #True)
  EndIf
  DisableGadget(#mainBtn_Sync, #True)
  OpenDestDir()
EndProcedure

;- Main
LoadSettings()
OpenMainWindow()
Delay(10)
OpenSourceDir()
OpenDestDir()
Repeat
  WindowEvent = WaitWindowEvent()
  WindowID    = EventWindow()
  GadgetID    = EventGadget()
  If WindowEvent = #PB_Event_Gadget
    If GadgetID = #mainBtn_DirSource
      SelectSourceDir()
    ElseIf GadgetID = #mainBtn_DirDest
      SelectDestDir()
    ElseIf GadgetID = #mainBtn_Check
      SelectCheckButton()
    ElseIf GadgetID = #mainBtn_ShowDiff
      SelectDiffButton()
    ElseIf GadgetID = #mainBtn_Sync
      SelectSyncButton()
    ElseIf GadgetID = #mainChk_incDirs
      If OptionIncDirs
        OptionIncDirs = #False
      Else
        OptionIncDirs = #True
      EndIf
    ElseIf GadgetID = #mainChk_incSubdirs
      If OptionIncSubdirs
        OptionIncSubdirs = #False
      Else
        OptionIncSubdirs = #True
        SetGadgetState(#mainChk_incDirs, #True)
      EndIf
    ElseIf GadgetID = #mainChk_incSystem
      If OptionIncSysfiles
        OptionIncSysfiles = #False
      Else
        OptionIncSysfiles = #True
      EndIf
    ElseIf GadgetID = #mainChk_incHidden
      If OptionIncHidfiles
        OptionIncHidfiles = #False
      Else
        OptionIncHidfiles = #True
      EndIf
    EndIf
  ElseIf WindowEvent = #PB_Event_CloseWindow
    If WindowID = #toolWnd
      SelectDiffButton()
    ElseIf WindowID = #mainWnd
      QUIT = #True
    EndIf
  EndIf
Until QUIT = #True
;- End
SaveSettings()
End  
Das einzige was mich noch stört, das dass Fenster beim Start nicht richtig aufgebaut wird, wenn bereits in den EInstellungen Pfade gespeichert sind.
---
Windows 11 (64 bit)
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Zwei Linked Lists vergleichen

Beitrag von ts-soft »

Nur als Hinweis:
Das Procedure.b ist unnötig und hat eher geschwindigkeitsminderte Auswirkungen. Procedure.i ist da schon
sinnvoller, evtl. schneller und #True und #False sind sowieso keine Byte :wink:

Gruß
Thomas
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8808
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Zwei Linked Lists vergleichen

Beitrag von NicTheQuick »

Makke hat geschrieben:@NickTheQuick: Danke für Dein Beispiel, das werde ich mal genauer unter die Lupe nehmen. Aber eine Frage habe ich, warum arbeitest Du bei der Prozedurübergabe mit Zeigern und übergibst die Liste nicht direkt. Persönliche Vorliebe oder birgt das irgendwelche Vorteile ?
Wenn du genau hinschaust, übergebe ich eine Variable der Struktur 'Directory', nicht 'DirectoryEntryDescriptor'. In Directory ist noch das Wurzelverzeichnis gespeichert. Im angehängten Beispiel nutze ich das zwar nicht, aber nur damit kann man aus dem relativen Pfades auch den absoluten "errechnen".
Geschwindigkeitsvorteile bringt das übrigens nicht. Nur der Algorithmus an sich.

Und noch was wegen den ganzen 'CompilerIf's:
Unter Windows sind die Dateien "Hallo.txt" und "hallo.TXT" die selben. Unter Linux nicht. Deswegen muss ich hier genauer differenzieren wie ich die Dateien vergleiche. Außerdem gibt es unter Linux kein Dateiattribut für versteckte Dateien. Da wird einfach nur ein . (Punkt) voran gestellt.
Antworten