AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Scripting Help (https://forums.alliedmods.net/forumdisplay.php?f=11)
-   -   Edit a word inside a text file (https://forums.alliedmods.net/showthread.php?t=262995)

aron9forever 05-16-2015 19:07

Edit a word inside a text file
 
I've been struggling for the past hour, but with no use. I just can't understand how files work in amxx. Maybe because I'm tired.

Anyways, suppose we have a file inside amxmodx/configs/messages.txt
and the contents of messages.txt are
PHP Code:

What a nice day
Problems
Good night 

I want to change "Problems" into "No problems" then rewrite the file.

How would I do this? I tried reading the whole file as a string then using replace_all and I failed miserably.
Code snips would also be very welcome

fysiks 05-16-2015 22:58

Re: Edit a word inside a text file
 
Read the file. Change the appropriate words. Write to the file (make sure to overwrite original contents).

Quote:

Originally Posted by aron9forever (Post 2297586)
I failed miserably.

What does that mean?

Quote:

Originally Posted by aron9forever (Post 2297586)
Code snips would also be very welcome

Quid pro quo. You basically just said "do this for me".

HamletEagle 05-17-2015 04:08

Re: Edit a word inside a text file
 
You can take a look inside BombStatus plugin, it has the feature to remove specific line from file. You can adapt that and work with it.

If you are too lazy:
PHP Code:


public plugin_init()
{
    
ChangeLine("Problems""No Problems")
}

stock ChangeLine(const OldLine[], const NewLine[])
{
    new const 
FileName[] = "file_testing.ini"
    
new const TempFileName[] = "tempfile.ini"

    
new ConfigDirPath[128]; get_configsdir(ConfigDirPathcharsmax(ConfigDirPath))
    new 
FullPath[256]; formatex(FullPathcharsmax(FullPath), "%s/%s"ConfigDirPathFileName)

    new 
FilePointer fopen(FullPath"rt")
    if(
FilePointer)
    {
        new 
TempFilePath[256]; formatex(TempFilePathcharsmax(TempFilePath), "%s/%s"ConfigDirPathTempFileName)
        
        new 
InputFilePointer fopen(TempFilePath"wt")
        if(
InputFilePointer)
        {
            new 
FileData[128]
            while(!
feof(FilePointer))
            {
                
fgets(FilePointerFileDatacharsmax(FileData))
                
trim(FileData)
                
                if(
equal(FileDataOldLine))
                {
                    
copy(FileDatacharsmax(FileData), NewLine)
                }
                
fprintf(InputFilePointer"%s^n"FileData)
            }
            
            
fclose(InputFilePointer)
            
fclose(FilePointer)

            
delete_file(FullPath)
            
rename_file(TempFilePathFullPath1)
            
            return 
1
        
}
    }
    return 
0


Note that this is just an example, to show you how it could be done. Further things depend on how you will use it(if you are going to call it many times, it would be good to cache all changes that needs to be done inside an array and do it during plugin_end and adjust the string on the fly).

aron9forever 05-17-2015 09:01

Re: Edit a word inside a text file
 
here's how I tried to do it. @Hamlet, I need to change a word not a whole line, but I will make it work with your way
It's only called once so no need to cache stuff
PHP Code:

new filepointer fopen("addons/amxmodx/configs/hello.txt","r+")
        
/*check if file is open,on an error filepointer is 0*/
        
if(filepointer)
        {
            new 
readdata[512]
            
/*Read the file until it is at end of file*/
            
while(fgets(filepointer,readdata,511))
            {   
                
replace_all(readdata511"Problems""No problems")
            }
            
fputs(filepointer,readdata)
            
fclose(filepointer)
        } 

I understand how files, pointers and cursor works in PHP, but I couldn't even read the whole file in a string, so I'll try what you posted now

HamletEagle 05-17-2015 09:38

Re: Edit a word inside a text file
 
Then:
PHP Code:

stock ChangeWord(const OldWord[], const NewWord[])
{
    new const 
FileName[] = "file_testing.ini"
    
new const TempFileName[] = "tempfile.ini"

    
new ConfigDirPath[128]; get_configsdir(ConfigDirPathcharsmax(ConfigDirPath))
    new 
FullPath[256]; formatex(FullPathcharsmax(FullPath), "%s/%s"ConfigDirPathFileName)

    new 
FilePointer fopen(FullPath"rt")
    if(
FilePointer)
    {
        new 
TempFilePath[256]; formatex(TempFilePathcharsmax(TempFilePath), "%s/%s"ConfigDirPathTempFileName)
        
        new 
InputFilePointer fopen(TempFilePath"wt")
        if(
InputFilePointer)
        {
            new 
FileData[128]
            while(!
feof(FilePointer))
            {
                
fgets(FilePointerFileDatacharsmax(FileData))
                
trim(FileData)
                
                if(
contain(FileDataOldWord) != -1)
                {
                    
replace(FileDatacharsmax(FileData), OldWordNewWord)
                }
                
fprintf(InputFilePointer"%s^n"FileData)
            }
            
            
fclose(InputFilePointer)
            
fclose(FilePointer)

            
delete_file(FullPath)
            
rename_file(TempFilePathFullPath1)
            
            return 
1
        
}
    }
    return 
0


Change the give word everytime it occurs.

aron9forever 05-17-2015 09:50

Re: Edit a word inside a text file
 
I get how it works now. You're reading line by line, then checking the lines, then writing line by line on a new file.
Isn't it possible to read the entire file into memory then rewrite the original file? (asking out of curiosity, this method is just fine)

fysiks 05-17-2015 13:18

Re: Edit a word inside a text file
 
Have you tried LoadFileForMe()?

Bugsy 05-23-2015 10:49

Re: Edit a word inside a text file
 
This will replace every instance of szFind with szReplaceWith in a file. Keep in mind that this reads the file in DATA_BUFFER_SIZE chunks and if szFind is located between chunks then it will not get replaced. Easiest way to avoid this is to size DATA_BUFFER_SIZE larger than the file, or use a method that reads 1 line at a time. If you're using 1.8.3+, use replace_string() instead of replace_all().

Edit: Updated it to handle szReplaceWith[] that is larger than szFind[]. If szReplaceWith is larger, it will use half of the data buffer for reading file data, and the other half to allow for the extra characters in szReplaceWith. If szReplaceWith is <= szFind, it will use the full buffer for reading file data. If OP can give more details about the potential size of this file, I can probably tweak it to be more reliable. If all else fails, a line-by-line replacer may be better.

PHP Code:

ReplaceTextInFile"my_file.txt" "find this" "replace with this" );
//Returns 1 on success, 0 on fail. 

PHP Code:

ReplaceTextInFile( const szFile[] , const szFind[] , const szReplaceWith[] )
{
    const 
DATA_BUFFER_SIZE 1024;
    
    new 
iSrcFile iDestFile szDestFile64 ] , szBufferDATA_BUFFER_SIZE ] , iLen iBytesRead iMaxBytesToRead;
    
    
formatexszDestFile charsmaxszDestFile ) , "%s_tmp" szFile );
    
    if ( ( 
iSrcFile fopenszFile "r" ) ) && ( iDestFile fopenszDestFile "w+" ) ) )
    {
        
iMaxBytesToRead = ( strlenszReplaceWith ) > strlenszFind ) ) ? ( DATA_BUFFER_SIZE ) : charsmaxszBuffer );
        
        while ( ( 
iBytesRead fread_blocksiSrcFile szBuffer iMaxBytesToRead BLOCK_CHAR ) ) )
        {
            
szBufferiBytesRead ] = EOS;
        
            
replace_allszBuffer charsmaxszBuffer ) , szFind szReplaceWith );
            
iLen strlenszBuffer );

            
fwrite_blocksiDestFile szBuffer iLen BLOCK_CHAR );
        }
        
        
fcloseiSrcFile );
        
fcloseiDestFile );
    }
    
    return ( 
iSrcFile && iDestFile && delete_fileszFile ) && rename_fileszDestFile szFile ) );



Bugsy 05-23-2015 18:49

Re: Edit a word inside a text file
 
Quote:

Originally Posted by .Dare Devil. (Post 2300267)
@Bugsy, your code doesn't make sense at all but still going to point out some things:
*What if replaced word is inside your block but is larger than old word, that much larger that it will need to go
to the new block?

Sorry that you don't understand it but try it and see, it does work. The only issue that I'm aware of at this point is:
  • When an szFind[] value gets cut-off between file reads around the buffer boundary location. Half of szFind will be at the end of one read while the other half will fall in the beginning of the next read. If the size of this file is known before-hand then the buffer can be sized accordingly to avoid this issue.
  • When szReplaceWith[] length exceeds szFind[] length and there is not enough room in the buffer, this should only impact files that have a size close to, or multiples of, the buffer size. I didn't realize this while writing the code and testing. My tests all used a szReplaceWith that was shorter than szFind. I will fix this issue. * Code updated *
Quote:

Originally Posted by .Dare Devil. (Post 2300267)
The block size should be the string size of szFind and then
read one block from pos 0, check it, then read another block from pos 1 and so on...
or:
block size can also be szFind * x, it would be faster.
x = (filesize /size:szFind) * size:zsFind.
Still tho, pawn is ... horrible language, not sure how you can hold the block size of that huge.
For example 1024*10
while writing/updating word then
if string size:szFind => string size:szReplaceWith
You have to move all chars back by: size:szFind - string size:szReplaceWith, from the end of to the file, to the find pos of:szFind

if size:szFind =< string size:szReplaceWith then same but opposite way.

replace function will only work if you read the whole file into your block.

I don't have the patience to understand what you're trying to say. Feel free to write it as you think is better and we can take a look.

aron9forever 05-24-2015 08:01

Re: Edit a word inside a text file
 
thanks for posting guys, but I used ham's implementation with reading line by line


All times are GMT -4. The time now is 19:37.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.