ruby-mine

exploring the mine

Rubinrote Strings - Teil 2: Fancy Strings

von murphy am 03.04.2006 (02 Uhr)

Syntax-Sonntag, verspätet. Ich hatte bis eben noch Urlaub. Heute geht es, wie versprochen, um

Fancy Strings

Ja, von sowas träumen Java- und Pascal-Programmierer nachts: Nützliche kleine syntaktische Zuckerwürfel, die einem Ruby-Programmierer das Leben erleichtern. Außerdem würde Obfuscation ohne Fancy Strings nur halbsoviel Spaß machen.

Kostproben:

%q(Es gibt 4 verschiedene Anführungszeichen: "'`´.)
%r{ [A-Z] \\w{5} \\b }x
plugin_paths += %W( #{VENDOR}/html_plugins ) unless XHTML
%Q'Instanzvariablen können #@eingebaut werden,\\
  #@@klassen_variablen\\n und #$globale auch.'
%r\\VERY STRANGE!\\x
%%%%%%%.empty?

Syntax

Grundsätzlich sieht ein Fancy String so aus:

Typen

%(...) (wird interpretiert)
%Q*ark daz* (wird interpretiert)
Das entspricht den doppelten Anführungszeichen (Quoted):
%Q(abc\d), %(abc\d) und "abc\d" liefern alle denselben String.
%q.:. (wird nicht interpretiert)
Das ist ein uninterpretierter String in einfachen Anführungszeichen, also genau dasselbe wie ':'.
%r-/\*.*?\*/-m (wird interpretiert)
Das erzeugt dieselbe Regexp wie /\/\*.*?\*\//m, nur kann man sie ein wenig besser lesen. (Oder?)
Modifier werden wie gewohnt angegeben; diese Regexp frisst übrigends C-Kommentare.
%x<ruby -e "p 2 ** `ls *.* | wc -l`"> (wird interpretiert)
Das ist ein Systemstring, der genau wie `rm -rf ~` einfach ausgeführt (eXecuted) wird.
%w]John James Sean\ Paul] (wird nicht interpretiert)

Dieser wohl bekannteste Typ erzeugt String-Arrays. Die Zeichenkette wird an jedem Leerzeichen getrennt; maskierte Leerzeichen werden ignoriert. Das Beispiel erzeugt ["John", "James", "Sean Paul"].
Besonders gerne benutze ich diese String mehrzeilig, als Ersatz für Datenfelder:

PREDEFINED_CONSTANTS = %w[
  nil true false self
  DATA ARGV ARGF __FILE__ __LINE__
]

Oder, in Rails:

inflect.uncountable %w( elvis\\ presley chaos penner )

Man erkennt, warum matz diesen Buchstaben gewählt hat: %w erzeugt Wortlisten.

%W~Wolf#$$ Tiger#{Time.now.sec}~ (wird interpretiert)
Dasselbe, diesmal wird jedes Wort noch einzeln interpretiert. Die Analogie ist klar: Q und W sind die interpretierenden Varianten von q und w.
Das Beispiel erzeugt bei mir ["Wolf3356", "Tiger49"], und bei euch sicher etwas anderes ($$ ist die aktuelle Prozessnummer.)
%s=Also, ich mag keine Tiger...= (wird interpretiert)
Symbole, beliebig lang, mit beliebigen Zeichen. Ja, solche Symbole gibt es.
Als bekanntere Alternative könnte man hier :"diese Variante" benutzen; Allerdings kennt Rdoc die Syntax nicht und gibt Fehler aus (':' not followed by identified or operator). Die uninterpretierte Variante mit einfachen Anführungszeichen mag Rdoc ebensowenig: :'Das ist mir egal!'

Die Tabelle für Technokraten

Typ erzeugt interpr. Besonderheiten
String ja keine alphanumerischen Zeichen als Begrenzer erlaubt
Q String ja
q String nein
w String-Array nein Leerzeichen müssen maskiert werden
W String-Array ja dito
s Symbol ja
x Ergebnis des Systemaufrufs als String ja
r Regexp ja Modifier können angehängt werden

Spezielle Begrenzer

Relativ einfach ist zu erkennen, wie Ruby mit Klammern umgeht:

%r[irgendwie logisch]
%q(Klammern (sowas hier: ()) kann man schachteln.)
%s>Achtung! Hier wartet Ruby nicht auf < sondern auf >

Verwirrender ist allerdings, dass Zeilenwechsel als Begrenzer dienen dürfen:

p %q
test

#-> "test"

Und ganz komisch wird es, wenn man Zeichen verwendet, die Ruby normalerweise interpretiert. Hier gibt Ruby dem Begrenzer immer den Vorrang.

%Q#Jetzt kann ich keinen \\#{Inline-Code} mehr benutzen!#
%\\Sieht aus wie eine Regexp im Spiegel.\\

Obfuscation

Als Beispiel für Code, den keiner lesen kann, benutze ich das folgende Ornament:

%%%%%%%

Hierbei handelt es sich um zwei Fancy Strings, die mit dem Operator % verknüpft werden; beide Fancy Strings sind simple Q-Strings, die als Begrenzer verwirrenderweise das Prozentzeichen verwenden. Also: %%% == "", so einfach. Und "" % "" ergibt wieder einen leeren String.

Woher weiß ich, dass Ruby nicht noch mehr Fancy Strings kennt, die murphy mir nicht verraten will?

(0..255).map do |c|
  type = c.chr
  #next unless type[/[a-z]/i]
  type if `ruby -ce 'p %#{type}(ls)'`['Syntax OK']
end.display

Liefert bei mir exakt den String " ')=QWqrswx". Die Erklärung der 4 ersten Zeichen bleibt dem Leser als Übungsaufgabe überlassen.

Viel Spaß mit Ruby!


Kommentar schreiben

Name (notwendig)

Mail (wird nicht veröffentlicht)

Webseite


Kommentare

  1. Malte schrieb am 03.04.2006 (16 Uhr)

    Da hat der Murphy doch glatt einen Fehler eingebaut: »Dann folgt ein beliebiger Begrenzer: Alles, was kein Whitespace ist, ist erlaubt [...]« irb> % abc => "abc" Nützlich war zu erfahren, daß %Q als % abgekürzt werden kann. Malte

  2. Murphy schrieb am 03.04.2006 (18 Uhr)

    Danke für deine Aufmerksamkeit, Malte! Ich habe es korrigiert. Ich hatte gerade nachgeschaut, ob ich das CodeRay noch beibringen kann, aber dann färbt er mir praktisch jeden %-Methoden-Aufruf als String.

  3. WoNáDo schrieb am 10.04.2006 (20 Uhr)

    Hi murphy! Leider funktionieren alphabetische Zeichen auch nicht wenn sie nach einem expliziten Format eingegeben werden (Stand OneClickInstaller 1.8.2) Beispielsweise liefert (in fxri getestet): irb(main):001:0> %Qabba SyntaxError: compile error (irb):1: unknown type of %string %Qabba ^ from (irb):1 from :0 Aber - ein schöner Beitrag! Motiviert mich direkt meine Sachen noch lesbarer zu machen...

  4. Murphy schrieb am 11.04.2006 (20 Uhr)

    Du hast recht, WoNáDo! Interessant. Jetzt greift das Argument nicht mehr ganz, dass man zukünftige Fancy-Typen reservieren wollte. Oder sind am Ende mehrbuchstabige Fancys geplant? %yaml(test) vielleicht, wie why the lucky stiff schon vorschlug? Wir sind gespannt...

  5. Murphy schrieb am 15.04.2006 (18 Uhr)

    %%%%%%%.empty? ist übrigends Blödsinn: Das wird als "" % false geparst und erzeugt einen Leerstring anstatt true.

  6. Ruby-Mine &raquo; Blog Archive &raquo; Rubinrote Strings - Teil 3: Heredocs schrieb am 19.04.2006 (10 Uhr)

    [...] Man benutzt Heredocs, um mehrzeilige Strings oder Strings mit sehr vielen Sonderzeichen oder beliebigem Text zu erzeugen. Im letzten Kapitel ging es ja um Fancy Strings, die einen beliebiges Zeichen als Begrenzer erlauben. Heredocs erlauben nun beliebige Strings als Begrenzer von Strings. [...]

  7. Ruby-Mine &raquo; Blog Archive &raquo; [2050°C] Der Anfang schrieb am 20.04.2006 (02 Uhr)

    [...] Murphy hat Euch in den letzten Wochen, einen tiefen Einblick in die syntaktischen Finessen von Ruby geliefert. Ihr habt normale Strings und Fancy Strings kennengelernt. Es stellte sich heraus, dass Here Docs ebenfalls nur Strings, mit einer zusätzlichen Dimension, sind. Die funktionale Programmierung mit proc Objekten macht Euch das Leben um einiges leichter. Und ein exception handling mit rescue ist nun zu einem selbstverständlichen Codefragment geworden, welches jeden unsicheren Code schmückt. Parallel hierzu möchten wir euch nun auf einer neuen Ebene einen Einblick in unseren Rubin verschaffen. Hierzu fahren wir die Schmelzanlage auf knapp 2050°C hoch und schauen uns an, was sich im inneren des Edelsteins befindet. [...]

  8. Ruby-Mine &raquo; Blog Archive &raquo; dont do &#8230; end schrieb am 09.07.2006 (19 Uhr)

    [...] Wie wäre es denn mit Fancy Strings? [...]