[Matlab] Gemischte Textdatei lesen und umformatieren

Müs Lee

Commodore
Registriert
Feb. 2007
Beiträge
4.903
Hallo zusammen,

ich möchte eine Inputdatei eines Simulationssolvers mittels Matlab lesen, bestimmte Textblöcke finden und diese Inhalte in ein anderes Dateiformat schreiben. Dabei stoße ich ständig an die Grenzen meines sehr beschränkten Matlabwissens, insbesondere wenn es um das korrekte Lesen und Kategorisieren des Textes geht. Die Inputdatei ist im ASCII-Format gehalten ohne einfache Delimiter wie ein Semikolon und mit gemischtem Text, das macht die Sache für mich als Quasi-Laien recht schwierig.

Aus besagter Inputdatei muss ich den Textblock auslesen, der nach dem ersten Auftauchen von /NODE folgt. Alle folgenden Instanzen von /NODE sind irrelevant und zu ignorieren. Danach benötige ich alle Blöcke, die nach /BEAM/i folgen, wobei i eine beliebige ganze Zahl ist (in diesem Fall 1, 3 und 4). Die Blöcke müssten in je ein Array gelesen und danach in einzelne Dateien geschrieben werden. Dabei habe ich primär das Problem des korrekten Auslesens bzw. der Umgang mit den Wiederholungen der /BEAM/i-Stichworte. Ich plage mich da recht viel mit der Konvertierung von char zu string hin zu numerical, dass die Übersicht verloren geht und ich nicht durchblicke, wie ich den BEAM-Block ordentlich einlesen soll, ohne dass sich das Skript ständig verhaspelt.

Wie man während des Einlesens des NODE-Blocks sieht, folgt die Inputdatei bei Blöcken der Konvention von 10 bzw. 20 Zeichen pro Eintrag. Da ich mangels besserer Alternative fgetl zur Lesen der Zeilen verwende, kommen die Infos als Charaktervektor rein und müssen/sollen zum Abgleich mit Schlüsselworten mittels strcmp zum Stringvektor überführt werden. Versuche einer Abfrage, ob die aktuelle Zeile Zahlenwerte enthält, schlugen fehl, da die Abfrage isnumeric(str2num('#')) komischerweise eine logische 1 ausspuckt. Die Krücke im Spoiler unten funktioniert zumindest für den Nodeblock. Wie aber gehe ich das im Beamblock anständig an? Die Inputdatei und das Wunschformat sind als txt im Anhang. P.S.: Warum haut fgetl bei Erreichen der letzten Zeile einen Fehler raus?

Primär gehts um das Lesen des Beamblocks, die Formatierung in das andere Format ist recht einfach. Sorry fürs Geschwurbel, bei Fragen einfach fragen.


Gekürzte Inputdatei zur Veranschaulichung:
Code:
#RADIOSS STARTER
##========================================================================================
##
## Radioss Input Deck Generated by HyperMesh Version  : 2021.0.0.33
## Generated using HyperMesh-Radioss Template Version : 2021.0.0.33
## Date: 08-31-2021   Time: 14:56:20
##
##========================================================================================
##
/BEGIN
lattice_test                                                                   
      2021         0
                  kg                   m                   s
                  kg                   m                   s
##
##HWCOLOR materials 1 8
/MAT/ELAST/1
steel                                                                                              
#              RHO_I
7.80000000000000E-09
#                  E                  nu
            210000.0                 0.3
##--------------------------------------------------------------------------------------------------
## NODES
##--------------------------------------------------------------------------------------------------
/NODE
         1        -24.99999809        -24.99999809        -24.99999809
         2        -24.99999809        -24.99999809               -15.0
         3        -24.99999809        -24.99999809                -5.0
         4        -24.99999809        -24.99999809                 5.0
         5        -24.99999809        -24.99999809                15.0
         6        -24.99999809        -24.99999809         24.99999809
...
       292                12.5                25.0               18.75
       293               18.75                25.0               18.75
       294                6.25                25.0               18.75
       295                12.5                25.0                6.25
       296               18.75                25.0                6.25
       297                6.25                25.0                6.25
       298                 0.0                50.0                 0.0
##--------------------------------------------------------------------------------------------------
## Quad Shell Elements
##--------------------------------------------------------------------------------------------------
/SHELL/2
       541       260       262       263       259                                                 
       542       259       263       256       257                                                 
       543       262       250       251       263                                                 
       544       263       251       252       256                                                 
       545       235       236       264       261                                                 
       546       261       264       262       260                                                 
...                                            
       601       252       279       297       253                                                 
       602       253       297       295       254                                                 
       603       279       278       291       297                                                 
       604       297       291       290       295                                                 
##--------------------------------------------------------------------------------------------------
## Direction Nodes (created) for BEAM3N Elements beacuse those were zeros
##--------------------------------------------------------------------------------------------------
/NODE
       299   -24.4226479310843   -24.4226479310843   -14.4226498410843
       300   -24.4226478208104   -24.4226478208104   -4.42264973081037
       301   -24.4226478208104   -24.4226478208104    5.57735026918963
       302   -24.4226478208104   -24.4226478208104    15.5773502691896
       303   -24.4226479310843   -24.4226479310843    25.5773482489157
/NODE
       304   -24.4226479310843   -14.4226498410843   -24.4226479310843
       305   -24.4226479310843   -14.4226498410843   -14.4226498410843
...
/NODE
       835    25.5773483591896    25.5773483591896    15.5773502691896
       836    25.5773482489157    25.5773482489157    25.5773482489157
/NODE
       837    25.5773482489157    25.5773482489157    25.5773482489157
/NODE
       838    25.5773482489157    25.5773482489157    25.5773482489157
##--------------------------------------------------------------------------------------------------
## Beam Elements
##--------------------------------------------------------------------------------------------------
/BEAM/4
         1         2         1       299
         2         3         2       300
         3         4         3       301
         4         5         4       302
         5         6         5       303
/BEAM/1
         6         7         1       304
         7         8         2       305
         8         8         7       306
         9         9         3       307
        10         9         8       308
        11        10         4       309
...
       523       210       209       821
/BEAM/3
       524       211       175       822
/BEAM/1
       525       211       205       823
/BEAM/3
       526       212       176       824
/BEAM/1
       527       212       206       825
/BEAM/3
       528       212       211       826
       529       213       177       827
/BEAM/1
       530       213       207       828
/BEAM/3
       531       213       212       829
       532       214       178       830
/BEAM/1
       533       214       208       831
/BEAM/3
       534       214       213       832
       535       215       179       833
/BEAM/1
       536       215       209       834
/BEAM/3
       537       215       214       835
       538       216       180       836
/BEAM/1
       539       216       210       837
/BEAM/3
       540       216       215       838
##HWCOLOR properties 2 24
/PROP/SHELL/2
property1                                                                                          
#   Ishell    Ismstr     Ish3n    Idrill                            P_Thick_Fail
        24         1                                                           
#                 Hm                  Hf                  Hr                  Dm                  Dn
                                                                                                   
#        N   Istrain               Thick              Ashear              Ithick     Iplas
         5                           1.0                                                 
##HWCOLOR properties 1 20
##HMBEAMSECASSOC  PROPASSOC        2
/PROP/BEAM/1
beams                                                                                              
#             Ismstr
                   
#                 Dm                  Df
                                       
#               Area                 Iyy                 Izz                 Ixx
    1.13097335529233   0.101787601976309   0.101787601976309   0.203575203952619
#     Wdof    Ishear
   000 000         0
##--------------------------------------------------------------------------------------------------
## Type 2 Interfaces
##--------------------------------------------------------------------------------------------------
##HMGROUP  1 7 SLVENT MSTENT
/INTER/TYPE2/1
group1                                                                                             
         3         4         0         1                   2                                     2.0
/GRNOD/NODE/3
GrnodPartForInterfaceId_1
        31        32        33        34        35        36        67        68        69        70
        71        72       103       104       105       106       107       108       139       140
       141       142       143       144       175       176       177       178       179       180
       211       212       213       214       215       216
/SURF/SEG/4
SurfSegForInterfaceId_1
                 260       262       263       259
...
                 297       291       290       295
/RWALL/PLANE/1
floor                                                                                              
         0         2         0         0
                 5.0                 0.2                 0.0                             0
                 0.0               -25.5                 0.0
                 0.0               -24.5                 0.0
/RWALL/SPHER/2
ball                                                                                               
       298         0         2         0
                 0.0                                    50.0
                0.01                 0.0             -2000.0                 0.0
##--------------------------------------------------------------------------------------------------
## Sets
##--------------------------------------------------------------------------------------------------
/GRNOD/NODE/1
floor_set                                                                                          
         1         2         3         4         5         6        37        38        39        40
        41        42        73        74        75        76        77        78       109       110
       111       112       113       114       145       146       147       148       149       150
       181       182       183       184       185       186
/GRNOD/NODE/2
top_set                                                                                            
       217       218       219       220       221       222       223       224       225       226
       227       228       229       230       231       232       233       234       235       236
       237       238       239       240       241       242       243       244       245       246
       247       248       249       250       251       252       253       254       255       256
       257       258       259       260       261       262       263       264       265       266
       267       268       269       270       271       272       273       274       275       276
       277       278       279       280       281       282       283       284       285       286
       287       288       289       290       291       292       293       294       295       296
       297
##--------------------------------------------------------------------------------------------------
## Parts
##--------------------------------------------------------------------------------------------------
##HWCOLOR components 1 3
/PART/1
beams                                                                                              
         1         1                             
##HWCOLOR components 2 6
/PART/2
shell                                                                                              
         2         1                             
##HWCOLOR components 3 9
/PART/3
top                                                                                                
         1         1                             
##HWCOLOR components 4 23
/PART/4
bottom                                                                                             
         1         1                             
##------------------------------------------------------------------------------
## End Of Radioss Block Deck
##------------------------------------------------------------------------------
/END
##HMNAME BEAMSECTCOLS           1beamsectcol1
##HMNAME BEAMSECTS
##HMCONT       1       1beamsection1
##HMCONT       2       7       1       01.0     1.0     0.0     0.0     0.0    
##HMCONT0.0            0
##HMNAME BEAMSECTS  BEAMSECTIONSTANDARD       11       1       0HMCircle
##HMNAME BEAMSECTS  BEAMSECTIONSTANDARD  PARAMETERS      0.5     0.5     10.0   
##HMNAME BEAMSECTS  END
##HMNAME BEAMSECTS
##HMCONT       2       1beamsection2
##HMCONT       2       7       1       01.0     1.0     0.0     0.0     0.0    
##HMCONT0.0            0
##HMNAME BEAMSECTS  BEAMSECTIONSTANDARD       11       1       0HMCircle
##HMNAME BEAMSECTS  BEAMSECTIONSTANDARD  PARAMETERS      0.6     0.5     10.0   
##HMNAME BEAMSECTS  END


Mein Versuch:
Code:
clear all
close all
clc

fid=fopen('lattice_test_0000.rad');
tic

if fid<0
    fprintf('Error while opening file\n')
end

node_exists=0;
i=1;
node_counter=1;

while fid>0
    current_line(node_counter)=string(fgetl(fid));
   
        if node_exists==1 && ~strcmp(current_line(node_counter),'##--------------------------------------------------------------------------------------------------')
%     if node_exists==1 && isnumeric(str2num(current_line(node_counter))) && ~isempty(str2num(current_line(node_counter)))
       
        lineValue=char(current_line(node_counter));
        nodeStuff(i,:)=[str2num(lineValue(1:10)),str2num(lineValue(11:30)),str2num(lineValue(31:50)),str2num(lineValue(51:70))];
        i=i+1;
       
     elseif node_exists==1 && strcmp(current_line(node_counter),'##--------------------------------------------------------------------------------------------------')
%     elseif node_exists==1 &&  isempty(str2num(current_line(node_counter))) %&& ischar(current_line(node_counter))
       
        node_exists=0;
        fprintf('End of /NODE block found at line %d\n',node_counter)
        break
       
    elseif strcmp(current_line(node_counter),'## End Of Radioss Block Deck')
        fprintf('## End Of Radioss Block Deck match found at line %d\n', node_counter)
        break
    end
   
    if strcmp(current_line(node_counter),'/NODE')
        node_exists=1;
        fprintf('Start of /NODE block found at line %d\n',node_counter)
    end
   
    node_counter=node_counter+1;
   
end

elem_exists=0;
elem_counter=1;
j=1;
while fid>0
    current_line(elem_counter)=string(fgetl(fid));

    if elem_exists==1 && ~strcmp(current_line(elem_counter),'##--------------------------------------------------------------------------------------------------')
        lineValue=char(current_line(elem_counter));
        beamStuff(j,:)=[str2num(lineValue(1:10)),str2num(lineValue(11:20)),str2num(lineValue(21:30)),str2num(lineValue(31:40))];
        j=j+1;

    elseif elem_exists==1 && strcmp(current_line(elem_counter),'##--------------------------------------------------------------------------------------------------')
        elem_exists=0;
        fprintf('End of /BEAM block found at line %d\n',elem_counter)
        break

    elseif strcmp(current_line(elem_counter),'## End Of Radioss Block Deck')
        fprintf('## End Of Radioss Block Deck match found at line %d\n', elem_counter)
        break
    end

    if strcmp(current_line(elem_counter),'/BEAM/')
        elem_exists=1;
        fprintf('Start of /BEAM block found at line %d\n',elem_counter)
    end
    elem_counter=elem_counter+1;
end

toc
fclose(fid);
 

Anhänge

  • lattice_test_0000.rad.txt
    99,7 KB · Aufrufe: 236
  • ntop_lattice.ltcx.txt
    42,8 KB · Aufrufe: 206
Sobald du doppelte Leerzeichen einstampfst liegt ja quasi Abschnittsweise ein CSV Format vor mit Leerzeichen als Delimiter.
Die Abschnitte greifst du dir mit regulären Ausdrücken.

Also
1. Regulären Ausdruck schreiben der das formal definiert, was du hier geschrieben hast Siehe unten, verwende lieber `extractBetween`und andere High-Level Funktionen.
2. Doppelte Leerzeichen entfernen
3. Als CSV parsen

Ggf. Recherchiere mal, was Matlab dir speziell anbietet alternativ zu Strings und Co. , ich bin spontan über extractBetween gestolpert, das erschlägt dir Schritt ggf. schon komplett. Es scheint ja nicht so schrecklich viele Varianten zu geben, die könntest du einfach händisch eingeben, das ist vermutlich simpler als mit regex zu arbeiten für dich.

Ein großes Problem besteht darin, dass du versuchst die Datei zeilenweise zu lesen. fileread scheint hier die angemessene Matlab-Funktion zu sein.
 
Zuletzt bearbeitet:
Danke für die Hinweise, das sieht vielversprechend aus :)

BeBur schrieb:
Sobald du doppelte Leerzeichen einstampfst liegt ja quasi Abschnittsweise ein CSV Format vor mit Leerzeichen als Delimiter.
Definitiv eine gute Idee, regexprep scheint dafür geeignet zu sein.

BeBur schrieb:
ich bin spontan über extractBetween gestolpert, das erschlägt dir Schritt ggf. schon komplett. Es scheint ja nicht so schrecklich viele Varianten zu geben, die könntest du einfach händisch eingeben, das ist vermutlich simpler als mit regex zu arbeiten für dich.
Das erleichtert die Aufgabe in der Tat schon sehr. Die paar Start-/Endworte manuell einzugeben ist kein Problem.

BeBur schrieb:
Ein großes Problem besteht darin, dass du versuchst die Datei zeilenweise zu lesen. fileread scheint hier die angemessene Matlab-Funktion zu sein.
Ich schaue es mir mal an. Den Vorteil von fgetl sah ich darin, dass die Datei häppchenweise vorkonfektioniert wird und damit einfach zu sezieren ist.
 
Prinzipiell gehe ich genau so vor und das sollte doch auch bei Beam funktionieren?

Das wuerde ich machen:
1) Textdatei mit fileread() einlesen
2) eingelesener Char-Vektor in seine Zeilen mit splitlines() in einen String-Vektor aufteilen
3) mit contains() nach \NODES oder \BEAM suchen. Bei \Nodes den ersten Treffer nehmen und anschließend nachdem nächsten "##" suchen und schneiden.
Bei Beam in der Schleife bis zum naechsten Treffer die schneiden und beim letzten entsprechend wieder nachdem naechsten "##" suchen.
--> Die Matrizen liegt nur noch als einzelne String-Vektor vor.
4) mit strip() Leerzeichen am Zeilenanfang und Ende entfernen
5) mit regexp() und passenden Trennzeichen (\t oder " ") den String-Vektor in eine String-Matrix auftrennen
6) String-Matrix mit str2double() in eine Double-Matrix umwandeln.
--> fertig
 
Frühzeitig in einzelne Zeilen aufteilen führt nur zu viel Mehraufwand, wenn sich der interessante Kontext auf mehrere Zeilen verteilt.
Ich glaube für Schritt 4-6 bietet Matlab high-level Funktionen an, die das übernehmen, Zahlen direkt in eine Matrix zu schreiben.
 
Ich fand/finde es intuitiver, weil es dann leichter zu lesen ist.
Ansonsten kann man natuerlich auch mit den Char-Vektor arbeiten.
Nur wenn es dann irgendwann hakt, kann es nervig werden.
In der Dimension macht es aus sicht der Performance auch keinen Unterschied.

Nachtrag:
Meiner Erfahrung nach wird der Schritt "str2double" der rechenintensivste sein und da ist es egal, welche Funktionen diesen ausfuehrt.
 
Zuletzt bearbeitet:
spielte die letzte zeit etwas oefter mit matlab rum; der code hier (forum nimmt keine .m dateien) scheint fuers einlesen zu funktionieren. die XML-ausgabe kriegste selbst hin.

beim groben druebergucken schien mir das zeilenweise durchlaufen noch am sinnvollsten zu sein. der vorteil von extractBetween erschliesst sich mir mangels funktion zum parsen nicht so recht. meine loesung liest alles aus, was:
  1. nach einer beam-/node-oeffnungszeile kommt
  2. vor der oeffnungszeile (alles was mit / beginnt) eines "fremden" blockes kommt
  3. beliebige anzahl von zahlenwerten pro zeile beinhaltet
 
Ok, ihr habt mich überzeugt auch einen Lösungs-Entwurf einzureichen ;).

Code:
file = fileread('lattice_test_0000.rad.txt');
node_data = extractBetween(file, '/NODE', '#');
node_data = str2num(node_data{1});
beam_data_start = regexpPattern("/BEAM/\d");
beam_data_end = ("/"|"#");
beam_data = extractBetween(file, beam_data_start, beam_data_end);
beam_data = cellfun(@str2num, beam_data, 'UniformOutput', false);
Die regulären Ausdrücke beam_data_start und beam_data_end müsste man sich nochmal angucken, es fehlt mindestens ein Block, nämlich der zweite (BEAM/1 ganz oben).
Wie gesagt, auf einzelnen Zeilen sollte man nur arbeiten, wenn einzelne Zeilen auch nur interessant ist. Dann würde man sich eben die 'interessanten' Zeilen rausfiltern und dann diese verarbeiten. Wenn man über mehrere Zeilen arbeiten will, dann überlässt man dem Computer die Arbeit und beschreibt ihm nur formal, was man will.

XML ist noch einfacher, dafür gibt es ja fertige parser.
 
Zuletzt bearbeitet:
Vielen Dank für eure Vorschläge und rege Anteilnahme :) Ich vergaß zu erwähnen, warum ich im ersten Versuch die Nodes zeil- und charweise einlesen ließ. Das Inputfile ist wie gesagt in Blöcke von 10, dann 20, 20, 20 Charaktere aufgeteilt, die teilweise ohne Leerzeichen direkt nebeneinanderstehen. Beim angehängten Beispiel kommt das nicht vor, bei anderen jedoch schon. Matlab liest dies dann ggf als eine Zahl mit integrierter Subtraktion aus. Beispiel:

Code:
##--------------------------------------------------------------------------------------------------
## NODES
##--------------------------------------------------------------------------------------------------
/NODE
         1         -149.9015962.22039065300000E-14         3.095395326
         2        -149.6887969-1.8702378310000E-08         6.171286582
         3        -149.6265106        -4.056159019         3.090819835
         4        -149.6265106         4.056159019         3.090819835
         5        -149.4760284-3.7404774380000E-08         9.247178077
         6        -149.3514251        -8.112317085         3.086243867
         7        -149.3514251         8.112317085         3.086243867
         8        -149.2632446-5.6107168680000E-08         12.32307052
         9        -149.0763244        -12.16847419         3.081667661
        10        -149.0763244         12.16847419         3.081667661
        11         -149.050476-7.4809562980000E-08         15.39896202
        12        -148.8377075-9.3511957280000E-08         18.47485542
        13         -148.801239        -16.22463226         3.077091693
        14         -148.801239         16.22463226         3.077091693
 
Krasser Bug da in dem Programm, was die Daten generiert. Wäre nicht shlecht gewesen, wenn dir das vorher einfällt ;).
textscan kommt offenbar damit klar. Ich würde die extrahierten Blöcke (extractBetween) also aufsplitten zeilenweise (splitlines), dann mit textscan parsen, dann wieder zusammen fügen.
Geht auch alles ohne Schleifen, in Matlab etwas unhandlich über die arrayfun und cellfun Funktionen, wobei nichts dagegen spricht, über die gefundenen Matches zu iterieren, solange die Logik des Programmes identisch bleibt.
Ohne Schleifen bleibt es nur etwas übersichtlicher, sind jeweils 2 Zeilen zusätzlich.
 
Müs Lee schrieb:
Code:
         1         -149.9015962.22039065300000E-14         3.095395326
         2        -149.6887969-1.8702378310000E-08         6.171286582
gut, das ist natuerlich in vielerlei hinsicht ungluecklich und da funktioniert mein regulaerer ausdruck nicht. natuerlich sind auch in den beam-bloecken die werte nur 10 zeichen bloecke statt der 20 in node-bloecken. naja, laeuft jedenfalls so.
 
BeBur schrieb:
Krasser Bug da in dem Programm, was die Daten generiert. Wäre nicht shlecht gewesen, wenn dir das vorher einfällt ;).
Wald und Bäume und so. Manchmal steckt man zu tief drin und vergisst, dass andere Leute manche wichtige Infos auch kennen müssen :) Ein Bug ist es nicht. Mit der genutzten Aufbereitungsmethode des Originalprogramms (RADIOSS) funktioniert es ja, und die damit interagierenden Programme können auch damit umgehen. Der Kern der meisten dieser Simulationsprogramme wurde in den 70ern bis 90ern gecoded und manche althergebrachte Sünden könnte man nur mit erheblichem Aufwand beheben. Die komplette Struktur müsste generalüberholt werden. Externe Programmen, die das einlesen müssen/können, müssten ebenfalls angepasst werden. Das nimmt niemand in Kauf.

@bog So elegant kann das also aussehen, schön :) Vielen Dank, jetzt muss ich es nur noch in /BEAM/1.../BEAM/N aufgeteilt kriegen.
 
Müs Lee schrieb:
@bog So elegant kann das also aussehen, schön :) Vielen Dank, jetzt muss ich es nur noch in /BEAM/1.../BEAM/N aufgeteilt kriegen.
sollte ja leicht genug gehen. bspw. durch regexp wie in der vorigen revision kannst du aus der /BEAM/ zeile die zahl extrahieren und dann durch ein wenig container maintenance separate matrizen fuer jeden beam aufbauen. ich wuerde vmtl. cells benutzen.
 
Zurück
Oben