Page 1 of 1

Adding sequential numbers to a file name

Posted: Sat Jun 02, 2018 12:54 pm
by Dreamland Fantasy
Hi there,

This procedure appends a sequential number to a file name in the format of "filename (n).jpg", where 'n' is the added number from 2 upwards. This is handy when e.g. copying files so that pre-existing files are not overwritten.

Code: Select all

Procedure.s AddSequentialNumber(FileName$)
  
  Protected Directory$, Extension$, i, j, n
  
  Directory$ = GetPathPart(FileName$)
  Extension$ = GetExtensionPart(FileName$)
  FileName$ = GetFilePart(FileName$, #PB_FileSystem_NoExtension)
  
  If Len(FileName$) > 4
    For i = Len(FileName$) To 1 Step -1
      If Mid(FileName$, i, 1) = "("
        For j = i To Len(FileName$)
          If Mid(FileName$, j, 1) = ")"
            n = Val(Mid(FileName$, i + 1, j - i - 1))
            If n
              FileName$ = RemoveString(FileName$, Mid(FileName$, i, j - i + 1), #PB_String_NoCase, i, 1)
              Break 2
            EndIf
          EndIf
        Next
      EndIf
    Next
  EndIf
  
  If n
    FileName$ = InsertString(FileName$, "(" + Str(n + 1) + ")", i)
  Else
    FileName$ = FileName$ + " (2)"
  EndIf
  
  FileName$ = Directory$ + FileName$
  If Extension$
    FileName$ + "." + Extension$
  EndIf
  
  If FileSize(FileName$) >= 0                   ; Checks existence of file
    FileName$ = AddSequentialNumber(FileName$)
  EndIf
  
  ProcedureReturn FileName$
  
EndProcedure

;- Test code below

FileName$ = OpenFileRequester("Select a file", "", "", 0)

For i = 1 To 10
  CopyFile(FileName$, AddSequentialNumber(FileName$))
Next
Kind regards,

Francis

Re: Adding sequential numbers to a file name

Posted: Sat Jun 02, 2018 1:05 pm
by Dude
Works well. I tried to break it and make it destroy a bunch of existing files, and it didn't. Good job! :mrgreen:

Re: Adding sequential numbers to a file name

Posted: Sat Jun 02, 2018 1:10 pm
by Dreamland Fantasy
Dude wrote:Works well. I tried to break it and make it destroy a bunch of existing files, and it didn't. Good job! :mrgreen:
Thanks for testing it out. :D

Kind regards,

Francis

Re: Adding sequential numbers to a file name

Posted: Sat Jun 02, 2018 2:03 pm
by Trond
Interesting. I just wrote something similar, so I wanted to compare it with your version. Which was a good idea, because it turned out neither of them worked optimally when the file didn't have an extension. (They added an extra dot.)

Also, my version started with the number 1, which, when thinking about it, isn't logical, as the original file would be number 1. So I changed it to start at 2.

My improved code:

Code: Select all

Procedure.s SafeFilename(OutFileName.s)
  NewFileName.s = OutFileName
  I = 1
  While FileSize(NewFileName) <> -1
    I + 1
    NewFileName = GetPathPart(OutFileName) + GetFilePart(OutFileName, #PB_FileSystem_NoExtension) + " (" + Str(I) + ")"
    Extension.s = GetExtensionPart(OutFileName)
    If Extension
      NewFileName + "." + Extension
    EndIf
  Wend
  If OutFileName <> NewFileName
    ;PrintN("Output file exists, using another filename: " + NewFileName)
    OutFileName = NewFileName
  EndIf
  ProcedureReturn OutFileName
EndProcedure

;- Test code below

FileName$ = OpenFileRequester("Select a file", "", "", 0)

For i = 1 To 10
  Debug AddSequentialNumber(FileName$)
  Debug SafeFilename(FileName$)
  ;CopyFile(FileName$, SafeFilename(FileName$))
  Debug ""
Next

Re: Adding sequential numbers to a file name

Posted: Sat Jun 02, 2018 4:59 pm
by Dreamland Fantasy
Thanks Trond for pointing out the bug if there was no extension. I've updated my code above to fix that.

I noticed with your code, if you use a file name such as "filename (2).jpg" then your code generates a file name of "filename (2) (2).jpg" instead of the next number up (e.g. "filename (3).jpg").

Kind regards,

Francis

Re: Adding sequential numbers to a file name

Posted: Sat Jun 02, 2018 7:28 pm
by davido
@Dreamland Fantasy,
Works well, thank you for sharing.

Re: Adding sequential numbers to a file name

Posted: Wed Jun 20, 2018 9:38 pm
by Dreamland Fantasy
Thanks for trying it out davido. :)

Kind regards,

Francis

Re: Adding sequential numbers to a file name

Posted: Thu Jun 21, 2018 12:34 pm
by NicTheQuick
I am not a big fan of recursion when it is not necessary. Also your code does not work correctly when there is an extension like "archive.tar.gz" but I understand that this is a limitation of GetExtensionPart() and GetFilePart(). For such cases one needs a list of valid extensions because there is no general rule what is an extension and what is not. "tar.gz" is a valid extension but what about "my.book.zip"? ;)

Edit:
More interesting things happen if you got a filename like "Chapter (1234 words).txt" and similar. I know, I know, it's hard to circumvent such issues and most of the time you can simply ignore them but it is important to know that this can happen.

Speaking RegEx: One idea is to really look for a string like "([0-9]+)" and not for "(.+)" . In your case RemoveString() destroys all it finds between the brackets ignoring all trailing characters when the first one is a number.

Re: Adding sequential numbers to a file name

Posted: Thu Jun 21, 2018 2:04 pm
by NicTheQuick
What do you think about this regular expression driven code?
You can also add special file extensions like "tar.gz" if you want to. I already added this as an example.
Also the pattern for numbering is " (number)" including the space at the beginning. So a filename like "test(4)" will not become "test(5)" in the next iteration. Instead it will be "test(5) (2)".

Code: Select all

EnableExplicit

Procedure.s AddSequentialNumber(filename.s)
	Static regExFindNumber.i = 0, regExFindExtension.i = 0
	If Not regExFindNumber
		regExFindNumber = CreateRegularExpression(#PB_Any, " \([1-9][0-9]*\)")
	EndIf
	If Not regExFindExtension
		regExFindExtension = CreateRegularExpression(#PB_Any, "\.[^\.]*$|.tar.gz$") ; Extend at will
	EndIf
	
	Protected matchPos.i = 0, matchLength.i = 0, matchNumber.i = 0
	If ExamineRegularExpression(regExFindNumber, filename)
		While NextRegularExpressionMatch(regExFindNumber)
			matchLength = RegularExpressionMatchLength(regExFindNumber)
			matchNumber = Val(Mid(RegularExpressionMatchString(regExFindNumber), 3, matchLength - 2))
			matchPos = RegularExpressionMatchPosition(regExFindNumber)
		Wend
	EndIf
	
	Protected result.s
	
	If matchLength
		Repeat
			matchNumber + 1
			result = Left(filename, matchPos + 1) + matchNumber + Mid(filename, matchPos + matchLength - 1)
		Until FileSize(result) < 0
	Else
		matchNumber = 1
		If ExamineRegularExpression(regExFindExtension, filename)
			If NextRegularExpressionMatch(regExFindExtension)
				matchPos = RegularExpressionMatchPosition(regExFindExtension)
				matchLength = RegularExpressionMatchLength(regExFindExtension)
				Repeat
					matchNumber + 1
					result = Left(filename, matchPos - 1) + " (" + matchNumber + ")" + Mid(filename, matchPos)
				Until FileSize(result) < 0
			EndIf
		EndIf
		
		If Not matchNumber
			Repeat
				matchNumber + 1
				result = filename + " (" + matchNumber + ")"
			Until FileSize(result) < 0
		EndIf
	EndIf
	
	ProcedureReturn result
EndProcedure

;- Test code below

Define i.i, filename.s = OpenFileRequester("Select a file", "", "", 0)
If filename

	For i = 1 To 10
		CopyFile(filename, AddSequentialNumber(filename))
	Next
EndIf

Re: Adding sequential numbers to a file name

Posted: Fri Jun 29, 2018 10:56 pm
by Dreamland Fantasy
NicTheQuick wrote:I am not a big fan of recursion when it is not necessary. Also your code does not work correctly when there is an extension like "archive.tar.gz" but I understand that this is a limitation of GetExtensionPart() and GetFilePart(). For such cases one needs a list of valid extensions because there is no general rule what is an extension and what is not. "tar.gz" is a valid extension but what about "my.book.zip"? ;)

Edit:
More interesting things happen if you got a filename like "Chapter (1234 words).txt" and similar. I know, I know, it's hard to circumvent such issues and most of the time you can simply ignore them but it is important to know that this can happen.

Speaking RegEx: One idea is to really look for a string like "([0-9]+)" and not for "(.+)" . In your case RemoveString() destroys all it finds between the brackets ignoring all trailing characters when the first one is a number.
Thanks for pointing out those issues. I haven't had a chance to look at them yet, but I will.

Kind regards,

Francis