|
-
Jul 6th, 2000, 05:05 PM
#1
Thread Starter
New Member
Is there any way that I can remove the last 128 bytes of a
binary file, without having to re-write the whole file?
-
Jul 6th, 2000, 07:55 PM
#2
Fanatic Member
If your using Dynamic String Variables (Length = 0 to approximately 2 billion) then YES
If your using Fixed length String Variables (Length = 1 to approximately 65,400) AND the length of the file is less than 64k then YES
Simply read your file whole into a dynamic string and extract the last 128 Bytes
Code:
'read file into string
FileString = Input(filelen(MyFile), #1)
'extract 128 bytes from right
My128Bytes = right$(FileString,128)
DocZaf
{;->
[Edited by Zaf Khan on 07-06-2000 at 08:57 PM]
-
Jul 6th, 2000, 08:46 PM
#3
Thread Starter
New Member
Thanks, but I dont think that is what I was trying to do... let me be more specefic. I am writing a program to edit ID3 tags on MP3 files. I have already written the part to read/edit the ID3 tags, but I'm having trouble writing the part to remove the ID3 tag. I need to actually delete the last 128 bytes of the file to remove the ID3 tag, but I don't want to have to re-write the whole file because MP3 files can be quite large. I know this can be done because Winamp can do it... Im just not sure how I would do it in Visual Basic.
-
Jul 6th, 2000, 09:06 PM
#4
Hyperactive Member
Maybe it does re-write the whole file
It doesn't take very long to re-write a file to local disk (missing the last 128 bytes), so maybe they do in fact do that and you didn't know?
I cannot think of any operating system that lets you truncate a file without (at some point) having to re-write the file.
Perhaps I am showing my ignorance here, but if it was possible, I presume you would have to get into the OS API (presumably windows) and get the FAT/NTFS entry for the file, and tell it that it is 128 bytes shorter than it currently thinks... I doubt whether this type of operation is going to be worth the effort since for one thing, it is potentially virus-like behaviour and maybe won't run smoothly on some people's machines...
Like I said, I am probably showing my ignorance so take anything I say with a couple dozen grains of salt!
Cheers
Paul Lewis
-
Jul 6th, 2000, 09:56 PM
#5
Thread Starter
New Member
Well actually I am pretty sure Winamp doesn't re-write the whole file, I tried copying a 9 meg mp3 file on my hard drive, and it took a little over a second. When I press the Remove ID3 Tag button in Winamp, there is very little hard drive activity, the HD activity light flashes just once for a fraction of a second. I do appreciate the responses though.
-
Jul 6th, 2000, 10:27 PM
#6
Fanatic Member
Maybe it's just writing null or Chr(0) into the area!
Just open the file for binary, start a PUT at FileLen - 128 and put 128 nulls!
Is the file length actually 128 bytes shorter after winamp makes the modification?
Remember that if winamp has the file open it may already be in memory so it can just dump the new contents.
Paul Dwyer 
Network Engineer
Aussie In Tokyo
Using Powerbasic 6 & VB6 SP4 (Please also add your VB Version to your signature!)
-
Jul 6th, 2000, 11:36 PM
#7
Hyperactive Member
I'm stuck then
I did a quick test on this PII 350 at work running NT4 with FAT file system (in need of a good optimise too I might add).
I tested with a 17MB file, reducing it by 128 bytes and the fastest I write it (assuming I do not count the 2 second read time) is 4 seconds.
I might try at home on my faster machine, which has a 7200rpm drive...I still don't think I will go from 4 seconds to "a fraction of a second".
Once you solve it, tell us about it because it will undoubtedly require a few API calls to manipulate the file system in ways unavailable to us mere mortals 
Good Hunting
Paul Lewis
-
Jul 6th, 2000, 11:55 PM
#8
Thread Starter
New Member
In response to Paul282, yes the file is actually 128 bytes shorter when Winamp removes the ID3 tag.
-
Jul 7th, 2000, 12:29 AM
#9
Fanatic Member
Are you using the string method above or a byte array? (Which IS faster!)
This problem intrigues me though, there should be an API call to truncate a file.
Paul Dwyer 
Network Engineer
Aussie In Tokyo
Using Powerbasic 6 & VB6 SP4 (Please also add your VB Version to your signature!)
-
Jul 7th, 2000, 12:43 AM
#10
Hyperactive Member
I was using byte array
I tried to use a string but I didn't know how to avoid the string descriptor being written to the file as well. Wehn I tried to use a fixed length string by creating a string of the length of the file (17MB) I ran out of string space (duh).
The byte array has always served me well i the past, but of course I was dealing with smaller files of under 500KB (usually JPG or BMP files).
It's after 5:30PM here on Friday so I'll check in again on Monday morning to see what has developed
Cheers All!
Paul Lewis
-
Jul 7th, 2000, 01:21 AM
#11
Fanatic Member
He Shoots, He Scores !!!!
Try using the
SetEndOfFile() API, or if the file is closed, SetFileAttributes()
With SetEndOfFile you just move the file pointer to where you want the EOF marker moved to.
Here's some documentation I found...
http://leb.net/wine/WinDoc/msdn/sdk/.../src/f78_9.htm
Paul Dwyer 
Network Engineer
Aussie In Tokyo
Using Powerbasic 6 & VB6 SP4 (Please also add your VB Version to your signature!)
-
Jul 7th, 2000, 01:46 AM
#12
Fanatic Member
He Shoots, He Scores !!!!
OK,
Here's the code, this chops a file to exacly 10000 bytes. I'm sure you can modify it to your needs (This took a while this my Dan Appleman API book)
Also notice the ByVal statement in the SetFilePointer API for lpDistanceToMoveHigh! This is necessary if the value is set to zero (mostly) but the API view will not include it!
Code:
'Module code
Public Type OFSTRUCT
cBytes As Byte
fFixedDisk As Byte
nErrCode As Integer
Reserved1 As Integer
Reserved2 As Integer
szPathName As String * 128 '(OFS_MAXPATHNAME) As Byte
End Type
Public Const OF_READ = &H0
Public Const OF_READWRITE = &H2
Public Const FILE_BEGIN = 0
Public Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, ByVal lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Public Declare Function SetEndOfFile Lib "kernel32" (ByVal hFile As Long) As Long
Public Declare Function OpenFile Lib "kernel32" (ByVal lpFileName As String, lpReOpenBuff As OFSTRUCT, ByVal wStyle As Long) As Long
' Form Code
Private Sub Command1_Click()
Dim OF As OFSTRUCT
Dim FileHandle As Long
Dim Ofset As Long
Dim SetOk As Long
FileHandle = OpenFile("C:\test.txt", OF, OF_READWRITE)
Ofset = SetFilePointer(FileHandle, 10000, 0, FILE_BEGIN)
SetOk = SetEndOfFile(FileHandle)
End Sub
During my search, I can across this for doing in in Qbasic... for those interested (I didn't write or test the QB code below though...)
Code:
CONST NumRec = 10
TYPE TheType
i AS INTEGER
END TYPE
DIM SHARED TheDim AS TheType
' Use the following include file for Visual Basic for MS-DOS:
REM $INCLUDE: 'VBDOS.BI'
' Use the following include file for QuickBasic for MS-DOS:
'$INCLUDE: 'QB.BI'
' Use the following include file for Basic PDS for MS-DOS:
'$INCLUDE: 'QBX.BI'
CALL CreateFile
CALL PrintFile
CALL ProcessFile(7)
CALL PrintFile
'################### File specific SUB #############################
SUB ProcessFile (LastRec%)
' Get the information about "TEST.DAT" and also the information
' about the location of the LASTREC% where the file will be truncated.
OPEN "test.dat" FOR RANDOM AS #1 LEN = LEN(TheDim)
FilePointer% = LastRec% * LEN(TheDim)
IF FilePointer% <> Truncate%(FILEATTR(1, 2), FilePointer%) THEN
PRINT "error..."
END IF
CLOSE
END SUB
'################## Generic INT file truncator ####################
FUNCTION Truncate% (handle%, FilePointer%)
' Generic function that will truncate any file at specified offset.
'
' Receives:
' 1. a MS-DOS file handle;
' 2. a file pointer offset in bytes, pointing where to truncate
' Returns:
' 1. If successful, position of file pointer; or
' 2. If error, flags register
'
DIM Regs AS RegType
Regs.ax = &H4200 ' INT 21h, service 42h
Regs.bx = handle% ' MS-DOS Handle
Regs.dx = FilePointer% ' Offset in bytes from start.
CALL Interrupt(&H21, Regs, Regs)
IF Regs.ax <> FilePointer% THEN ' AX returns pointer position.
Truncate% = Regs.flags ' If error, return flag.
END IF
Regs.ax = &H4000 ' INT 21h, service 40h
Regs.bx = handle% ' MS-DOS handle
Regs.cx = &H0
CALL Interrupt(&H21, Regs, Regs)
IF Regs.ax <> 0 THEN ' Must be zero bytes written.
Truncate% = Regs.flags ' If error, return flag.
END IF
Truncate% = FilePointer% ' Return offset location.
END FUNCTION
'############ Create Sample File using RANDOM Access ################
SUB CreateFile
KILL "test.dat"
OPEN "test.dat" FOR RANDOM AS #1 LEN = LEN(TheDim)
FOR j = 1 TO NumRec
TheDim.i = j
PUT #1, j, TheDim
NEXT j
CLOSE #1
END SUB
'########### Print contents of TEST.DAT ##################
SUB PrintFile
OPEN "test.dat" FOR RANDOM AS #1 LEN = LEN(TheDim)
Max = LOF(1) / LEN(TheDim)
FOR j = 1 TO Max
GET #1, j, TheDim
PRINT TheDim.i
NEXT j
CLOSE
END SUB
...And have a good weekend !!
[Edited by Paul282 on 07-07-2000 at 02:58 AM]
Paul Dwyer 
Network Engineer
Aussie In Tokyo
Using Powerbasic 6 & VB6 SP4 (Please also add your VB Version to your signature!)
-
Jul 7th, 2000, 02:39 AM
#13
Fanatic Member
Oops
I forgot, The code above requires the CloseHandle API to close the file, VB close() won't work. If your app is terminating after the truncate then this is not a big deal...
bad habits have a way of biting me sometimes though... please add it.
Paul
Paul Dwyer 
Network Engineer
Aussie In Tokyo
Using Powerbasic 6 & VB6 SP4 (Please also add your VB Version to your signature!)
-
Jul 7th, 2000, 03:02 AM
#14
Thread Starter
New Member
Great! It works perfectly. Thanks a lot. Also, I realized that CloseHandle was missing from your example and I was going to come back to tell you, but I see you already realized that
-
Jul 7th, 2000, 03:46 AM
#15
Thread Starter
New Member
Thanks again, I just added it to my ID3 tag editor program and it works exactly like I wanted it to
-
Jul 7th, 2000, 08:17 AM
#16
transcendental analytic
Nice work Paul!! Why didn't you come up with this the two times i posted this qwestion?
Use  
writing software in C++ is like driving rivets into steel beam with a toothpick.
writing haskell makes your life easier:
reverse (p (6*9)) where p x|x==0=""|True=chr (48+z): p y where (y,z)=divMod x 13
To throw away OOP for low level languages is myopia, to keep OOP is hyperopia. To throw away OOP for a high level language is insight.
-
Jul 7th, 2000, 09:50 AM
#17
Fanatic Member
haha, dunno!
Something just seemed very wrong here that it couldn't be done, I searched the net and even the delphi forums had people writing new files. Then I came across the Qbasic code on the MS site but couldn't find anything else.
In the end I turned to Dan! Mr Applemans very-difficult-to-learn-from API encyclopedia is a good reference but explanations are a little short. I was writing code by opening file-X for binary as #1 but there was nothing to pass to the SetEndOfFile API.
That's what I hate about about API (To Quote Star Wars -Yoda)
"One you head down the API path, forever does it dominate your destiny"
You want to use one little API but then there's the contants and the Data Types and the other calls to get the parameters you need... Why is this not in VB already?
Think I'll add a Truncate() function to my DLL.
Well enough patting myself on the back, time for another Sangria.
Paul Dwyer 
Network Engineer
Aussie In Tokyo
Using Powerbasic 6 & VB6 SP4 (Please also add your VB Version to your signature!)
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|