0

I've had a look at How do I re.search or re.match on a whole file without reading it all into memory? and TypeError: sequence item 1: expected a bytes-like object, str found , but the solutions in those two questions have not worked for me.

So the goal of this is to do a regex search in the text file for a pattern, specifically from the example text file

ParameterValue = Texture2D'/Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC.T_BEHead01_BC' ParameterValue = Texture2D'/Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC.T_BEHead01_BC'

I want to extract "/Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC" and "/Game/Characters/Slashers/Bear/Textures/Outfit01/T_BEHead01_BC" which I know to be the string regex pattern:

Texture2D\'(.*)\.

and part of the capture group .group(1) .

The relevant code section is:

     with open(mytool.propstxt_path, 'r+') as f:
            data = mmap.mmap(f.fileno(), 0)
            match_obj = re.match('Texture2D\'(.*)\'', data)
            print(match_obj.group(1))

and am using mmap to read the entire text file to memory so data acts like a normal string that can be regex matched.

However I'm getting a problem: cannot use a string like pattern on a bytes like object on this line: match_obj = re.match('Texture2D'(.*)'', data)

So I tried converting the string pattern to a bytes like pattern with regex and compile. But unfortunately this causes the match_obj to be nothing because printing it returns None, so I think the following code segment is incorrect.

 with open(mytool.propstxt_path, 'r+') as f:
            data = mmap.mmap(f.fileno(), 0)
            regex = rb'Texture2D\'(.*)\''
            pattern = re.compile(regex)
            match_obj = re.match(pattern, data)#'Texture2D\'(.*)\''
            print(match_obj)

I have also tried putting a b before the pattern to convert it to bytes, but this again causes the match_obj to be None with no attribute .group().

with open(mytool.propstxt_path, 'r+') as f:
            data = mmap.mmap(f.fileno(), 0)
            match_obj = re.match(b'Texture2D\'(.*)\'', data)
            print(match_obj.group(1))

Final thing I've tried is decoding the data to UTF-8 using .decode(). by doing the following, but it returns an error mmap.mmap object has no attribute decode.

with open(mytool.propstxt_path, 'r+') as f:
            data = mmap.mmap(f.fileno(), 0).decode('UTF-8')
            match_obj = re.match('Texture2D\'(.*)\'', data)
            print(match_obj.group(1))

The final thing I'm thinking of trying is just reading the whole file line by line as mmaps mean you need to use byte like regex which I don't know how to do in my case.

My whole code is the following, but a warning it is for a blender plugin, creating a panel and such.

#import all libraries including those needed for regex matching and mapping string to memory 
import bpy, re, mmap

class PathProps(bpy.types.PropertyGroup):
    propstxt_path: bpy.props.StringProperty(name="Select PropsTxt File ", description="Select a File", subtype="FILE_PATH")
    texloc_path: bpy.props.StringProperty(name="Select Exported Game Folder ", description="Select a Folder", subtype="DIR_PATH")

class DBDShaderScript_PT_main_panel(bpy.types.Panel):
    bl_label = "DBD Shader Maps"
    bl_idname = "DBDShaderScript_PT_main_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "DBD Shaders"
    
    def draw(self, context):
        layout = self.layout
        scene = context.scene
        
        mytool = scene.my_tool
        
        layout.prop(mytool, "propstxt_path")
        layout.prop(mytool, "texloc_path")
        
        layout.operator("dbdshaderscript.addbasic_operator")
        

class DBDShaderScript_OT_add_basic(bpy.types.Operator):
    bl_label = "Add Basic Shader Map (Every Material Except Hair)"
    bl_idname = "dbdshaderscript.addbasic_operator"
    def execute(self, context):
        
        #make reference to user inputted file name
        scene = context.scene
        mytool = scene.my_tool
        
        #find the selected material, s_mat stands for selected material
        s_mat = bpy.context.active_object.active_material
        s_mat.use_nodes = True
        
        #store new link function to variable
        link = s_mat.node_tree.links.new
        
        #assign Principled BSDF to a variable so can be referenced later
        #so that nodes can link to it
        principled_node = s_mat.node_tree.nodes.get('Principled BSDF')
        
        #start adding all nodes and respective links to shader map
        srgb_node =  s_mat.node_tree.nodes.new("ShaderNodeSeparateRGB")
        srgb_node.location = (-200,150) # x,y
        link(srgb_node.outputs[2], principled_node.inputs[4])
        
        node =  s_mat.node_tree.nodes.new("ShaderNodeValToRGB")
        node.location = (-500,50) # x,y
        link(node.outputs[0], principled_node.inputs[7])
        
        #node =  s_mat.node_tree.nodes.new("ShaderNodeSeparateRGB")
        #node.location = (-200,100) # x,y
        #link(node.outputs[2], principled_node.inputs[4])
         
        with open(mytool.propstxt_path, 'r+') as f:
            data = mmap.mmap(f.fileno(), 0)
            match_obj = re.match('Texture2D\'(.*)\'', data)
            print(match_obj.group(1))
        
        #add image texture nodes
        texImage = s_mat.node_tree.nodes.new('ShaderNodeTexImage')
        #example loading image
        #texImage.image = bpy.data.images.load("C:\\Users\\myName\\Downloads\\Textures\\Downloaded\\flooring5.jpg")
        
        #uncomment this
        #texImage.image = bpy.data.images.load(match_obj.group(1)[0])
        
        
        
        #make a link to principle BSDF node
        link(texImage.outputs[0], principled_node.inputs[0])
        
        return {"FINISHED"}


#register + unregister all classes with a loop
classes = [PathProps, DBDShaderScript_PT_main_panel, DBDShaderScript_OT_add_basic]
 
 
 
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
        
        #create pointer to all properties
        bpy.types.Scene.my_tool = bpy.props.PointerProperty(type = PathProps)
 
def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)
        del bpy.types.Scene.my_tool
 
 
if __name__ == "__main__":
    register()

John Smith
  • 333
  • 2
  • 3
  • 9
  • 1
    The regex and file reading code in "whole file" looks fine if the file is a text file. If the file is binary, you need to use `rb` instead of `r+` and define the pattern as `rb"Texture2D'(.*?)'"` – Wiktor Stribiżew Apr 10 '21 at 10:02

1 Answers1

0

Okay so I solved my problem by not using mmaps altogether because they're annoying requiring bitwise regular expressions which I did not like. So what I did is just read the entire file into a string, of course this wouldn't work for longer files, but it was all good for my purposes.

Here's my solution I just replaced this bit.

#with just means open and close file
with open(mytool.propstxt_path, 'r') as f:
    #read entire file to one string
    data = f.read()
    #find all matches through regex to the string Texture2D' with capture group 
    #any character zero to unlimited times and ending with '
    #also store capture groups into a list variable
    match_list = re.findall("Texture2D\'(.*)\'", data)
John Smith
  • 333
  • 2
  • 3
  • 9