Dosya İşlemleri (Karşılaştırma)

Merhabalar,

Öncelikle karşılaştırma derken, nasıl bir karşılaştırma yapacağımızı anlatmalıyım. Performans işlemleri karşılaştırma kriterlerim içerisinde bulunmuyor, bundan hiç bahsetmeyeceğim. Ama kodun okunabilirliği, hızlı yazılması, hatalara karşı ne kadar duyarlı olduğu ve tabii ki en önemlisi tekrar kullanabilirliği.

Aslında bu son nokta yani ‘tekrar kullanılabilirlik’ baÅŸlı başına bir yazı konusu ama buna ÅŸimdilik pek deÄŸinmeyeceÄŸim. BaÅŸlıkta yazdığı gibi dosya iÅŸlemlerini karşılaÅŸtıracağım.

Arşılaştırmayı sadece VBScript, JScript ve Ruby arasında yapacağım, neden mi ? hali hazırda yapmıştım da ondan.. bu yazıyı yazacağım diye oturup program yazmadım. Önce programları yazdım, sonra yazıyı yazmak aklıma geldi. Bu nedenle neden diye sormayın. :)

Ama belki daha sonraki zamanlarda Java ve C# versiyonlarının karşılaştırmalarını da eklerim.

Öncelikle sizlere problemden bahsedeyim.

Belirli bir server üzerinde ps ve txt (post script ve text) dosyaları online bir uygulama tarafından oluÅŸturuluyor, daha sonrasında online uygulama üzerinden kullanıcı bu dosyaları temizlemeyi unutuyor ve dosya sisteminin ÅŸiÅŸmesi ile beraber performans problemleri ortaya çıkıyor. Bu nedenle dosyaların gün bazında ömürlerinin olmasına ve ömrünü doldurmuÅŸ olan dosyalarında sistem tarafından silinmesine, silme iÅŸlemi esnasında log almasını ve bu loglarında aynı ömür kuralına tabii olmasını istiyoruz… iÅŸte bu program bu iÅŸi yapacak.. günde bir kez çalışacak ve bu iÅŸlemi yapacak. Microsoft Windows sistemde çalışmasını istediÄŸimiz için VBScript ve JScript’de (JavaScript’in bire bir aynı klonu) ve platform bağımsız olan Ruby’de yazıldı. Ä°ÅŸte Ruby örneÄŸi;

# Sabitler büyük harf ile tanımlanır.
SOURCE_DRIVE_NAME = 'C'
SOURCE_FOLDER_NAME = 'C:\FtpData'
SOURCE_FILE_TYPES = 'txt,ps'
LOG_FILE_NAME = 'Schedule_Job.txt'
EXPIRY_DAY = 7

source_files = nil
if SOURCE_FILE_TYPES.include?(",")
	source_files = File.join("**", "*.{#{SOURCE_FILE_TYPES}}")
else
	source_files = File.join("**", "*.#{SOURCE_FILE_TYPES}")
end
# Belirtilen dizin mevcut mu ?
if File.directory?(SOURCE_FOLDER_NAME) == false
	raise Exception.new("Folder does not exist (#{SOURCE_FOLDER_NAME})")
end
# Loglama işlemi için One Click-installer ile otomatik olarak gelen log4r kullanildi.
require "log4r"

toDay = Time.now
log = Log4r::Logger.new('IS19660')
logFile = Log4r::FileOutputter.new('IS19660', :filename => SOURCE_FOLDER_NAME + '\\' + toDay.strftime('%Y%m%d%H%M%S') + '_' + LOG_FILE_NAME, :trunc => true)
log.outputters = logFile
log.level = Log4r::INFO

fileCTime = nil
diff = 0.00
# Dizine geçildi 
Dir.chdir(SOURCE_FOLDER_NAME)
# Dizin içerisinde ilgili dosyalar bulundu ve üzerinden gezildi.
Dir.glob(source_files) do |fileName|
	fullFileName = SOURCE_FOLDER_NAME + "\\" + fileName.gsub('/','\\')
	# Bulunan dosyanın dizin olmadığı garanti altına alındı.. bazı durumda
	# örnek dosya isminde 2 tane '.' olması durumunda bize dosya olarak dizinleri veriyor.
	next if FileTest.directory?(fullFileName)
	fileCTime = File.ctime(fullFileName)
	diff = (toDay - fileCTime) / 86400
	if diff.floor >= EXPIRY_DAY
		begin
			File.delete(fullFileName)
		rescue
			log.info "Error at #{fullFileName}: #{$!.message}"
		else
			log.info "Deleted : #{fullFileName} at #{fileCTime.strftime('%d/%m/%Y %H:%M:%S')}"
		end
	else
		log.info "Skipped (Day) : #{fullFileName} at #{fileCTime.strftime('%d/%m/%Y %H:%M:%S')}"
	end
end

Görüldüğü gibi toplamı 45 satır civarında ve işin yapıldığı kısım yaklaşık 15 satır. çok efektif ve anlaşılması çok kolay.

Ä°ÅŸte sizlere VBScript hali.

' Tüm değişkenlerin tanımlı olması şarttır.
Option Explicit

Const SourceDriveName = "C"
Const SourceFolderName = "C:\FtpData"
Const SourceFileTypes = "txt,ps"
Const LogFileName = "Schedule_Job.txt"
Const ExpiryDay = 7

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Constants for opening files
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Const OpenFileForReading = 1 
Const OpenFileForWriting = 2 
Const OpenFileForAppending = 8 

Sub WriteLog(Str)
	If Str <> "" Then
		LogStr = LogStr & Str & NewLine
	Else
		LogStr = LogStr & Err.Number & " - " & Err.Description & NewLine
		LogStr = LogStr & Err.Source & NewLine
		Err.Clear
	End If
End Sub

Sub ProcessFile(Folder)
	Dim Files, File, Diff, Index
	Dim FileExtension, TempArr, Skip

	Set Files = Folder.Files
	If Files.Count > 0 Then
		For Each File In Files
			TempArr = Split(File.Name, ".")
			FileExtension = TempArr(UBound(TempArr))
			Skip = True
			For Index = LBound(FileTypes) To UBound(FileTypes)
				If FileTypes(Index) = FileExtension Then
					Skip = False
					Exit For
				End If
			Next
			
			If Skip = False Then
				Diff = DateDiff("d", File.DateCreated, ToDay)
				If Diff >= ExpiryDay Then
					WriteLog "Deleted: " & File.Path & " -- "  & File.DateCreated & " -- "  & ToDay & " -- "  & Diff
					File.Delete
					If Err.Number <> 0 Then
						WriteLog ""
					End If
				Else
					WriteLog "Skipped (Day) : " & File.Path & " -- "  & File.DateCreated & " -- "  & ToDay & " -- "  & Diff
				End If
			Else
				WriteLog "Skipped (Extension) : " & File.Path & " -- "  & File.DateCreated & " -- "  & ToDay
			End If
		Next
	End If
End Sub

Sub ProcessFolder(ParentFolder)
	Dim SubFolder, Folder
	
	Set SubFolder = ParentFolder.SubFolders
	If SubFolder.Count > 0 Then
		For Each Folder In SubFolder
			ProcessFolder Folder
		Next
	End If
	ProcessFile ParentFolder
End Sub

Sub Main()
	On Error Resume Next
	Dim SourceFolder, ToDayStr
	ToDayStr = Year(ToDay) & Right("00" & Month(ToDay), 2) & Right("00" & Day(ToDay), 2) & Right("00" & Hour(Time), 2) & Right("00" & Minute(Time), 2) & Right("00" & Second(Time), 2)
	Set FSO = CreateObject("Scripting.FileSystemObject")

	If FSO.DriveExists(SourceDriveName) AND FSO.FolderExists(SourceFolderName) Then
		Set SourceFolder = FSO.GetFolder(SourceFolderName)
		ProcessFolder SourceFolder

		If Err.Number <> 0 Then
			WriteLog ""
		End If

		If Trim(LogStr) <> "" Then
			Dim LogFile
			Set LogFile = FSO.CreateTextFile(SourceFolderName & "\" & ToDayStr & "_" & LogFileName, OpenFileForWriting, True)
			LogFile.Write LogStr
			LogFile.Close
		End If
	Else
		Dim LogFileInC

		Set LogFileInC = FSO.CreateTextFile("C:\" & ToDayStr & "_" & LogFileName, OpenFileForWriting, True)
		LogFileInC.Write LogStr
		LogFileInC.Write Err.Number & "-" & Err.Description & NewLine
		LogFileInC.Write Err.Source & NewLine
		LogFileInC.Close
	End If
End Sub

Dim NewLine, TabChar, LogStr, ToDay
Dim FSO, FileTypes

NewLine = Chr(10)
TabChar = Chr(9)
LogStr = ""
ToDay = Date
FileTypes = Split(SourceFileTypes, ",")

Call Main()

Yaklaşık 115 satır, programın kendisi yaklaşık 80 satır kadar, içerisinde recursive bir method mevcut, bu nedenle takip etmesi ve anlaması oldukça zor, fakat kütüphane desteÄŸi yüksek. Bununla beraber çok ofansif bir program, herhangi bir hata yakalama yok, ‘On Error Resume Next’ ile hayatın çok güzel olduÄŸu varsayılmış.

Åžimdi JScript (JavaScript versiyonu)

var sourceDriveName = 'C';
var sourceFolderName = 'C:\\FtpData';
var sourceFileTypes = 'txt,ps';
var logFileName = 'Schedule_Job.txt';
var expiryDay = 7;
/*
constants for opening files
*/
var OpenFileForReading = 1;
var OpenFileForWriting = 2;
var OpenFileForAppending = 8;

var logStr = '';
var fileTypes = new Array();
var toDay = new Date();

function print(str) {
	logStr += (str + '\n');
}
// En azından sınıflar çalışma zamanında değiştirilebiliyor.
Number.prototype.fixDigit = function (fixDigit$) {
	if (arguments.length != 1) {
		throw new Error('Wrong number of arguments');
	}
	var length = arguments[0];
	var retValue = this.toString();
	var currentLength = retValue.length;
	if (currentLength >= length) {
		return retValue;
	}
	for (var index = 0; index < length - currentLength; index++) {
		retValue = '0' + retValue;
	}
	return retValue;
}
// format işlemini kendi kullanımıma göre yazdım.
Date.prototype.format = function(format$) {
	return this.getFullYear().toString() + this.getMonth().fixDigit(2) + this.getDate().fixDigit(2) + ' ' + 
		   this.getHours().fixDigit(2) + ':' + this.getMinutes().fixDigit(2) + ':' + this.getSeconds().fixDigit(2);
}

// Off offf VBScript'in DateDiff'i var, JScript'in yeni oldu.. internetten buldum ama sonra bir ÅŸekilde bulduÄŸum yeri kaybettim :(.
// Bu nedenle nereden aldığımı yazamıyorum. Elbette bulduğum yerde bu şekilde bir prototype değildi. Bu hale ben getirdim.
Date.prototype.dateDiff = function (h$) {
	if (arguments.length != 2) {
		throw new Error('Wrong number of arguments');
	}

	var interval = arguments[0];
	var dt1 = arguments[1];
	var dt2 = this;

	var diffMS = dt2.valueOf() - dt1.valueOf();
	var diff = new Date(diffMS);

	// calc various diffs
	var years  = dt2.getUTCFullYear() - dt1.getUTCFullYear();
	var months = dt2.getUTCMonth() - dt1.getUTCMonth() + (years==0 ? 0 : years*12);
	var quarters = parseInt(months/3);
	
	var milliseconds = diffMS;
	var seconds = parseInt(diffMS/1000);
	var minutes = parseInt(seconds/60);
	var hours = parseInt(minutes/60);
	var days = parseInt(hours/24);
	var weeks = parseInt(days/7);

	switch(interval.toLowerCase()){
		case "yyyy": return years;
		case "q": return quarters;
		case "m": return months;
		case "d": return days;
		case "w": return days;
		case "ww": return weeks;
		case "h": return hours;
		case "n": return minutes;
		case "s": return seconds;
		case "ms":return milliseconds;
		default: return "invalid interval: '" + interval + "'";
	}
}

function processFile(folder) {
	var files = new Enumerator(folder.Files);
	for (; !files.atEnd(); files.moveNext()) { 
		var file = files.item();
		var fileName = file.Name;
		var fileCreateDt = new Date(file.DateCreated);
		var fileExtension = fileName.split('.').pop();
		var skipped = true;
		for (extension in fileTypes) {
			if (fileTypes[extension] == fileExtension) {
				skipped = false;
				break;
			}
		}

		if (!skipped) {
			if (toDay.dateDiff("d", fileCreateDt) >= expiryDay) {
				try {
					file.Delete();
					print('Deleted :' + fileName + ' - ' + fileCreateDt.format());
				} catch (e) {
					print('Error At ' + fileName + ':' + e.name + ' - ' + e.message);
				}
			} else {
				print('Skipped (Day) :' + fileName + ' - ' + fileCreateDt.format());
			}
		} else {
			print('Skipped (Extension) :' + fileName + ' - ' + fileCreateDt.format());
		}
	}
}

function processFolder(folder) {
	processFile(folder);
	var subFolders = new Enumerator(folder.SubFolders);
	for (; !subFolders.atEnd(); subFolders.moveNext()) { 
		processFolder(subFolders.item());
	}
}

function main() {
	var fso = new ActiveXObject('Scripting.FileSystemObject');
	fileTypes = sourceFileTypes.split(',');

	try	{
		if (fso.DriveExists(sourceDriveName) == false || fso.FolderExists(sourceFolderName) == false) {
			throw new Error('Folder or Drive does not exist.!!');
		}

		processFolder(fso.GetFolder(sourceFolderName));
	} catch (e) {
		if (typeof(e) == 'string') {
			print(e);
		} else {
			print(e.name + ' - ' + e.message);
		}
	}
	
	var toDayStr = toDay.getFullYear().toString() + (toDay.getMonth() + 1).fixDigit(2) + toDay.getDate().fixDigit(2);
	toDayStr += toDay.getHours().fixDigit(2) + toDay.getMinutes().fixDigit(2) + toDay.getSeconds().fixDigit(2);
	var logFile = null;
	try	{
		logFile = fso.CreateTextFile(sourceFolderName + '\\' + toDayStr + '_' + logFileName, OpenFileForWriting, true);
		logFile.Write(logStr);
	} catch (e) {
		if (logFile != null) {
			logFile.Close();
		}
		if (typeof(e) == 'string') {
			print(e);
		} else {
			print(e.name + ' - ' + e.message);
		}
		logFile = fso.CreateTextFile('C:\\' + toDayStr + '_' + logFileName, OpenFileForWriting, true);
		logFile.Write(logStr);
	} finally {
		if (logFile != null) {
			logFile.Close();
		}
	}

	return 0;
}

main();

Yaklaşık 160 satır, programın kendisi 130 satır civarında, recursive bir fonksiyona sahip, kütüphane desteği az olduğu için (neyseki prototype tabanlı) Date sınıfına dateDiff ve format methodları ile Number sınıfına fixDigit methodu eklenmiştir. Tamaniyle defansif bir yazım tekniği uygulanmıştır. prototype bazlı olduğu için dateDiff, format ve fixDigit methodları tekrar rahatlıkla kullanılabilir, hatta sizlerde kullabilirsiniz.

Sonuç olarak; Ruby açık ara önde, 2. sırada bence JScript geliyor(Hata yakalama işi önemli bir kavram), en son VBScript.

Belki gelecek yazılarda Java, C# ve Python versiyonlarını yazabilirim.. belli olmaz :)

Kolay gelsin…

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir