Ein paar Worte vorabHome   Letzte MeldungenNews   Index der Kapitel und der besprochenen FunktionenIndex   Wer ich bin, warum ich diese Seiten mache, KontaktImpressum   Ich freue mich über jeden Eintrag im Gästebuch!Gästebuch   Einige Links zu anderen AutoLisp-SeitenLinks   Copyrights und DisclaimerRechts
Hier können die kompletten Seiten als ZIP-File heruntergeladen werden!

Funktionen für komfortables Arbeiten mit Zeichenketten String-Tango
Noch mehr Funktionen für komfortableres Arbeiten mit Zeichenketten Kettenhunde
strtok zerlegt Zeichenketten anhand eines Trennzeichens Tock-Tock
Arbeiten mit Datum und Zeit in AutoLisp Zeitlos...
Dotted pairs - wie man den Programmabbruch verhindert Gepunktet?
Neue Funktionen für die Listenbearbeitung Strukturtapete
Weitere neue Funktionen für die Listenbearbeitung Listen to me!
Lambda expressions - dasSalz in der Suppe Lambada
Lambda expressions anhand eines Praxisbeispiels Unter der Erde
Where und whereever erleichtern den Umgang mit Listen Quo vadis?
Rekursion - Funktionen, die sich selbst aufrufen Katzenschwanz
Ein äusserst wichtiger Prototyp für Funktionen Nix passiert
Wo das lineare mapcar am Ende ist Tiefer rein!
Über Effekte und Neben(Seiten-)Effekte von Funktionen Seitensprünge
Die Namensräume (Sichtbarkeit) von Variablen Raumwunder
Let dient zur Schaffung kleinerer Namensräume Lass mal...
Sukzessive Verarbeitung von Listenresten mit mapcdr Der Bruder
Was in AutoLisp einfach nicht machbar ist (Teil I) Beschränkt
Was in AutoLisp einfach nicht machbar ist (Teil II) Limited Edition
Nicht mit Effekten arbeiten, sondern direkter Daten-Änderung Destruktiv
Sequenzielles vs. paralleles Abarbeiten von Argumenten Parallelwelten
Über den Umgang mit Funktionsschablonen Erwachet!
Ein Praxiskapitel über Auswahlsätze, Attribute, wcmatch und mehr Durch die Brust
Die Farben des AutoCAD Color Index und ihre RGB-Werte Alles so schön
Hier laufen die Fäden zusammen: Viele Konzepte vereint Lapsus Lispuli
Ein Spiel als Beispiel für lernfähige Funktionen Zug um Zug
Errorhandling in AutoLisp - Teil 1 Alles valsch!
Errorhandling in AutoLisp - Teil 2 Und foll Feler!
Regular expressions in AutoLisp - Teil 1 Menstruation?
Regular expressions in AutoLisp - Teil 2 Hasso, such!
Regular expressions in AutoLisp - Teil 3 Ersatzteile
Regular expressions in AutoLisp - Teil 4 Perlentaucher


Zum Einsteiger-Tutorial

Zu den ActiveX-Seiten

Meine Private HP mit Fotos, Gedichten, Musik und Postkartenversand

Mein Online-Lexikon der Fotografie

Mein völlig abgedrehtes Reisebüro

Meine bonbonfarbene Gedichteseite










Die PCRE-Bibliothek wurde von Philipp Hazel ursprünglich für das Exim-Projekt entwickelt, wird aber inzwischen von vielen Softwareprojekten verwendet, darunter die Sprachen PHP und Python. Die Bibliothek ist frei, auch für die Verwendung in kommerzieller Software. Einige Informationen findet man auf der PCRE-Heimseite, die allerdings erstaunlich sparsam gehalten ist.

Wer nach weiteren Informationen zu den regulären Ausdrücken sucht, ist dort allerdings völlig falsch - die gibt es natürlich in der Perl-Dokumentation! PCRE heisst ja schließlich 'Perl Compatible Regular Expressions', und bis auf ein paar kleine Abweichungen ist PCRE zu Perl völlig kompatibel. Ich selbst arbeite mit ActivePerl - aber man muss ja kein Perl haben, um mit PCRE zu arbeiten. Wer aber einen Blick in die Handbücher und Tutorials zu den regulären Ausdrücken werfen will, muss bei ActiveState schon das ganze Paket (ca. 50 MB Nettogröße, die aber ca. 150 MB auf der Platte belegen) herunterladen. Wer sich das nicht antun will, sollte allerdings mit Gugels Hilfe auch andere Tutorials usw. finden, z.B. die Seite regular-expressions.info. Wer sich mal eine etwas komplexere Lisp-Anwendung zu Gemüte führen möchte, die massiven Gebrauch der PCRE-Funktionalität macht, der kann sich mein Tippex-Programm anschauen, das bei Cadwiesel zum Download bereitliegt. Bei diesem Programm geht es darum, dass (als Vorbereitung für das Plotten) bestimmte Korrekturen an Maßtexten vorgenommen werden. So zeigt AutoCAD beispielsweise bei einer Maßtoleranz mit dem Bereich +0 -1 den Wert +0 oben hinter der Maßzahl an - nach deutschen Normen sollte diese 0 weggelassen werden und nur die Minustoleranz unten angezeigt werden. Dieser und andere Schönheitsfehler können mit 'Tippex' behoben werden. Das Programm ist leicht zu erweitern bzw. zu modifizieren, wenn man andere Fehler beheben möchte: man muss nur neue regular expressions eintragen - am Code selbst muss man gar nichts ändern. Allerdings, das sei nicht verschwiegen: Die regulären Ausdrücke können monströse Ausmaße annehmen;-)

Ich habe versucht, den Ball möglichst flach zu halten. Meine ganze Leistung besteht darin, dass ich a) den Quelltext der Datei "pcre2006.arx" geschrieben habe, und b) die Funktion (pcre-sr) in Lisp programmiert habe. Nur zwei Funktionen aus der Bibliothek (pcre_compile() und pcre_exec()) habe ich überhaupt verwendet. Damit die Bibliothek aus Lisp heraus aufgerufen werden kann, musste ich die RX-Schnittstelle (ehemals ADS) in AutoCAD verwenden, es handelt sich also nicht um eine ObjectARX-Anwendung.

Die Lisp-Implementation von (pcre-sr) ist rudimentär - Steuerzeichen werden ignoriert. In Perl steht z.B. das Zeichen '$' im Ersetzungsmuster für einen einzufügenden Zeilenumbruch. (pcre-sr) ignoriert das aber, das Dollarzeichen bleibt ein Dollarzeichen. Einen Zeilenumbruch im Ersetzen-Ausdruck muss man in AutoLisp also mit '\n' erzeugen.

Für alle, die es irgendwie interessieren sollte, hier der Code der DLL (pcre2006.arx) in C:

#include  <stdio.h>
#include  <adslib.h>
#include  <rxregsvc.h>
#include  <rxdefs.h>
#include  <aced.h>
#include  <acdbads.h>
#include  "pcre.h"


#define OVECCOUNT 30    /* should be a multiple of 3 */

#define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))

struct func_entry { char *func_name; int (*func) (struct resbuf *); };

int pcre_match       (struct resbuf *rb);

static struct func_entry func_table[] = { {"pcre-match", pcre_match},
                                        };
/*  Declarations of other local functions  */
void     main               (int, char **);
int      dofun              (void);
int      funcload           (void);


/* ACRXENTRYPOINT -- This function replaces main() for an ARX program. */

extern "C" AcRx::AppRetCode acrxEntryPoint(AcRx::AppMsgCode msg, void* appId){
    switch(msg) {
    case AcRx::kInitAppMsg:
        acrxDynamicLinker->unlockApplication(appId);
    acrxDynamicLinker->registerAppMDIAware(appId);
        break;
    case AcRx::kInvkSubrMsg:
        dofun();
        break;
    case AcRx::kLoadDwgMsg:
        funcload();
    }
    return AcRx::kRetOK;
}



/* FUNCLOAD  --  Define this application's external functions.  Return
                 RTERROR on error, else RTNORM.                   */

static int funcload(){
    int i;

    for (i = 0; i < ELEMENTS(func_table); i++) {
        if (!acedDefun(func_table[i].func_name, (short)i))
            return RTERROR;
    }
    return RTNORM;
}

/* DOFUN -- Execute external function (called upon an RQSUBR request).
            Return value from the function executed, RTNORM or RTERROR. */

static int dofun()
{
    struct resbuf *rb;
    int val;

    /* Get the function code and check that it's within range.
       (It can't fail to be, but paranoia doesn't hurt.) */
    if ((val = acedGetFunCode()) < 0 || val >= ELEMENTS(func_table)) {
        acdbFail("Received nonexistent function code.");
        return RTERROR;
    }

    /* Fetch the arguments, if any. */
    rb = acedGetArgs();

    /* Call the handler and return its success-failure status. */
    val = (*func_table[val].func)(rb);
    acutRelRb(rb);
    return val;
}


/*-----------------------------------------------------------------------*/
static int pcre_match(struct resbuf *rb){
  char* str;
  char* pat;
  char* flagstr = NULL;
  int flags = 0;
  struct resbuf* resultlist;
  struct resbuf* rover;
  pcre *re;
  const char *error;
  int erroffset;
  int ovector[OVECCOUNT];
  int rc, i;
  char* failstr = NULL;

  if((rb == NULL)||(rb->rbnext == NULL)){
    acdbFail("Too few arguments");
    return RTERROR;
  }

  if((rb->restype != RTSTR) || (rb->rbnext->restype != RTSTR)) {
    acdbFail("Wrong argument type");
    return RTERROR;
  }

  if(rb->rbnext->rbnext){
    if(rb->rbnext->rbnext->restype != RTSTR){
      acdbFail("Wrong argument type");
      return RTERROR;
    }else{
      flagstr = rb->rbnext->rbnext->resval.rstring;
      while(flagstr[0]){
        switch(flagstr[0]){
        case 'i':
          flags |= PCRE_CASELESS;
          break;
        case 'x':
          flags |= PCRE_EXTENDED;
          break;
        case 'm':
          flags |= PCRE_MULTILINE;
          break;
        case 's':
          flags |= PCRE_DOTALL;
          break;
        default:
          break;
        }
        flagstr++;
      }
    }
  }

  pat = rb->resval.rstring;
  str = rb->rbnext->resval.rstring;

  re = pcre_compile(
    pat,                  /* the pattern */
    flags,                /* options */
    &error,               /* for error message */
    &erroffset,           /* for error offset */
    NULL                  /* use default character tables */
  );

  /* Compilation failed: print the error message and exit */

  if (re == NULL){
    sprintf(failstr, "PCRE compilation failed at offset %d: %s\n", erroffset, error);
    acdbFail(failstr);
    return RTERROR;
  }

  /* Compilation succeeded: match the subject in the second argument */

  rc = pcre_exec(
    re,                   /* the compiled pattern */
    NULL,                 /* no extra data - we didn't study the pattern */
    str,                  /* the subject string */
    (int)strlen(str),     /* the length of the subject */
    0,                    /* start at offset 0 in the subject */
    0,                    /* default options */
    ovector,              /* output vector for substring information */
    OVECCOUNT             /* number of elements in the output vector */
  );

  /* Matching failed: handle error cases */

  if (rc < 0){
    switch(rc){
    case PCRE_ERROR_NOMATCH:
      acedRetNil();
      return RTNORM;
      break;
    /* Handle other special cases if you like */
    default:
      acedRetInt(rc);
      return RTNORM;
      break;
    }
  }

  /* Match succeded */
  resultlist = rover = acutNewRb(RTLB);
  for(i = 0; i < rc; i++){
    rover->rbnext = acutNewRb(RTLB);
    rover = rover->rbnext;
    rover->rbnext = acutNewRb(RTLONG);
    rover = rover->rbnext;
    rover->resval.rint = ovector[2*i];

    rover->rbnext = acutNewRb(RTLONG);
    rover = rover->rbnext;
    rover->resval.rint = ovector[2*i+1] - ovector[2*i];

    rover->rbnext = acutNewRb(RTLE);
    rover = rover->rbnext;
  }
  rover->rbnext = acutNewRb(RTLE);
  rover = rover->rbnext;
  rover->rbnext = NULL;

  acedRetList(resultlist);
  return RTNORM;

}