{"id":4524,"date":"2020-09-25T17:38:22","date_gmt":"2020-09-25T15:38:22","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=4524"},"modified":"2020-10-25T13:02:31","modified_gmt":"2020-10-25T11:02:31","slug":"insel-textbloecke-text-blocks","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2020\/09\/insel-textbloecke-text-blocks\/","title":{"rendered":"Insel: Textbl\u00f6cke (text blocks)"},"content":{"rendered":"\r\n<p>Strings mit einem Zeilenumbruch kommen in Programmen immer wieder vor, etwa bei Bildschirmausgaben, eingebettetem HTML, XML, JSON oder SQL. Ein Beispiel:<\/p>\r\n\r\n\r\n\r\n<pre>String joke =<br \/>\"Lehrer: '76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.'\\n\" +<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"Sch\u00fcler: 'Herr Lehrer, so viele sind wir doch gar nicht!'\";<\/pre>\r\n\r\n\r\n\r\n<p>Im ersten String-Literal steht \\n f\u00fcr den Zeilenumbruch. Das Pluszeichen setzt die beiden Strings zusammen. Der Java-Compiler stellt selbst\u00e4ndig aus den beiden Konstanten ein gr\u00f6\u00dferes String Literal her.<\/p>\r\n\r\n\r\n\r\n<p>In Java 15 wurden Textbl\u00f6cke eingef\u00fchrt (standardisiert im JEP 378: Text Blocks,<a href=\"https:\/\/openjdk.java.net\/jeps\/378\"> https:\/\/openjdk.java.net\/jeps\/378<\/a>). Damit lassen sich einfacher mehrzeilige Strings aufbauen. Drei doppelte Anf\u00fchrungszeichen leiten einen Textblock ein (opening delimiter genannt) und drei doppelte Anf\u00fchrungszeichen schlie\u00dfen einen Textblock wieder ab (closing delimiter genannt). Mithilfe von Textbl\u00f6cken sieht unser Beispiel von oben so aus:<\/p>\r\n\r\n\r\n\r\n<pre>String joke = \"\"\"<br \/>\u00a0\u00a0\u00a0Lehrer: '76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.'<br \/>\u00a0\u00a0\u00a0Sch\u00fcler: 'Herr Lehrer, so viele sind wir doch gar nicht!'\"\"\";<br \/>System.out.println( joke );<\/pre>\r\n\r\n\r\n\r\n<p>Ein Textblock wird immer mit drei doppelten Anf\u00fchrungszeichen eingeleitet, und ein Zeilenumbruch muss folgen. Das bedeutet automatisch, dass die Textbl\u00f6cke immer mindestens zwei Quellcodezeilen umfassen, und niemals nur in eine Zeile geschrieben werden k\u00f6nnen.<\/p>\r\n\r\n\r\n\r\n<p>Nach der Einleitung eines Textblocks h\u00e4ngt der Compiler an jede Zeile, die nicht mit drei Anf\u00fchrungszeichen abgeschlossen wird, einen Zeilenumbruch LINE-FEED, kurs LF (Unicode \\u000A). In unserem Beispiel haben wir nur einen Zeilenumbruch. H\u00e4tten wir die drei Anf\u00fchrungszeichen in die n\u00e4chste Zeile geschrieben, h\u00e4tten wir zwei Zeilenumbr\u00fcche im Ergebnis:<\/p>\r\n\r\n\r\n\r\n<pre>String joke = \"\"\"<br \/>\u00a0\u00a0\u00a0Lehrer: '76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.'<br \/>\u00a0\u00a0\u00a0Sch\u00fcler: 'Herr Lehrer, so viele sind wir doch gar nicht!'<br \/>\u00a0\u00a0\u00a0\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>Es macht also einen Unterschied, ob die drei Anf\u00fchrungszeichen zum Schlie\u00dfen des Textblocks in einer eigenen Zeile stehen (der entstehende String endet mit einem LF) oder am Ende eines Strings (der entstehende String endet nicht mit einem LF). Beim Schreiben w\u00fcrde das sicherlich etwas symmetrischer und h\u00fcbscher aussehen, wenn der Beginn eines Textblocks und das Ende eines Textblocks jeweils in einer einzelnen Zeile stehen w\u00fcrden, doch das k\u00f6nnen wir nicht immer machen, denn das ergibt immer am Ende einen Zeilenumbruch, der vielleicht nicht gew\u00fcnscht ist.<\/p>\r\n\r\n\r\n\r\n<p>Textbl\u00f6cke sind eine alternative Schreibweise f\u00fcr Strings in doppelten Anf\u00fchrungszeichen. Im Bytecode ist sp\u00e4ter nicht mehr zu erkennen, auf welche Art und Weise der String entstanden ist. Textbl\u00f6cke k\u00f6nnen genauso wie regul\u00e4re Strings an allen Stellen eingesetzt werden, an denen Strings gefordert werden, etwa als Argument:<\/p>\r\n\r\n\r\n\r\n<pre>System.out.println( \"\"\"<br \/>Lehrer: '76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.'<br \/>Sch\u00fcler: 'Herr Lehrer, so viele sind wir doch gar nicht!'\"\"\" );<\/pre>\r\n\r\n\r\n\r\n<p>Ist im Quellcode innerhalb vom Textblock ein Zeilenumbruch sinnvoll, damit z. B. die Zeile nicht so lang wird, l\u00e4sst sich mit einem Backslash der Zeilenumbruch im Ergebnisstring verhindern:<\/p>\r\n\r\n\r\n\r\n<pre>String joke2 = \"\"\"<br \/>\u00a0\u00a0Warum haben Giraffen so einen langen Hals? \\<br \/>\u00a0\u00a0Weil der Kopf so weit oben ist.\"\"\";<br \/>\/\/ Warum haben Giraffen so einen langen Hals? Weil der Kopf so weit oben ist.<br \/>System.out.println( joke2 );<\/pre>\r\n\r\n\r\n\r\n<p>Da Textbl\u00f6cke Strings sind, lassen sie sich auch mit dem +-Operator konkatenieren:<\/p>\r\n\r\n\r\n\r\n<pre>String person1 = \"Lehrer\", person2 = \"Sch\u00fcler\";<br \/>String joke = person1 +<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"\"\"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0: '76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.'<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"\"\" + person2 + \"\"\"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0: 'Herr Lehrer, so viele sind wir doch gar nicht!'\"\"\";<br \/>System.out.println( joke );<\/pre>\r\n\r\n\r\n\r\n<p>Es l\u00e4sst sich ablesen, dass das Einsetzen von Variableninhalten nicht besonders elegant ist &#8212; das war allerdings nicht das Ziel der Textbl\u00f6cke gewesen, so etwas wie String-Interpolation zu schaffen, was etwa JavaScript kann. Etwas zur Hilfe kommt die String-Methode formatted(&#8230;), die als Objektmethode hinzugekommen ist:<\/p>\r\n\r\n\r\n\r\n<pre>String person1 = \"Lehrer\", person2 = \"Sch\u00fcler\";<br \/>String joke = \"\"\"<br \/>\u00a0\u00a0\u00a0%s: '76 %% aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.'<br \/>\u00a0\u00a0\u00a0%s: 'Herr Lehrer, so viele sind wir doch gar nicht!'\"\"\".formatted( person1, person2 );<br \/>System.out.println( joke );<\/pre>\r\n\r\n\r\n\r\n<p>Die Einr\u00fcckung der Zeilen in einem Textblock spielt eine elementare Rolle. Die Zeilen eines Textblocks m\u00fcssen nicht ganz links am Anfang einer Zeile bei der Position 0 beginnen, sondern d\u00fcrfen rechts einger\u00fcckt sein und den \u00fcblichen Konventionen in der Einr\u00fcckung von Java Programmen folgen. Bei der Einr\u00fcckung sollte beil\u00e4ufiger Wei\u00dfraum (engl. incidental white space) aus dem eigentlichen Textblock ausgenommen werden. Hier wendet der Java-Compiler einen kleinen Algorithmus an. Die Regel ist, dass die Zeile die am weitesten links steht (die Zeile mit dem drei abschlie\u00dfenden Anf\u00fchrungszeichen geh\u00f6rt dazu) den beil\u00e4ufigen Wei\u00dfraum bestimmt, der abgeschnitten wird. Es ist dabei egal, ob das Zeichen ein Tabulator oder Leerzeichen ist, auch wenn das auf dem Bildschirm anders aussieht! Bestehen bleibt rechts von dieser Stelle der wesentliche Wei\u00dfraum (engl. essential white space).<\/p>\r\n\r\n\r\n\r\n<p>Beispiel 1:<\/p>\r\n\r\n\r\n\r\n<pre>String joke2 = \"\"\"<br \/>\u00a0\u00a0\u00a0Warum haben Giraffen so einen langen Hals?<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0Weil der Kopf so weit oben ist.\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>Ausgabe 1:<\/p>\r\n\r\n\r\n\r\n<p>Warum haben Giraffen so einen langen Hals?<\/p>\r\n\r\n\r\n\r\n<p>\u00a0\u00a0Weil der Kopf so weit oben ist.<\/p>\r\n\r\n\r\n\r\n<p>Beispiel 2:<\/p>\r\n\r\n\r\n\r\n<pre>String joke2 = \"\"\"<br \/>\u00a0\u00a0\u00a0Warum haben Giraffen so einen langen Hals?<br \/>Weil der Kopf so weit oben ist.\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>Ausgabe 2:<\/p>\r\n\r\n\r\n\r\n<p>\u00a0\u00a0\u00a0\u00a0Warum haben Giraffen so einen langen Hals?<\/p>\r\n\r\n\r\n\r\n<p>Weil der Kopf so weit oben ist.<\/p>\r\n\r\n\r\n\r\n<p>Beispiel 3:<\/p>\r\n\r\n\r\n\r\n<pre>String joke2 = \"\"\"<br \/>\u00a0\u00a0\u00a0\u00a0Warum haben Giraffen so einen langen Hals?<br \/>\u00a0\u00a0\u00a0\u00a0Weil der Kopf so weit oben ist.<br \/>\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>Ausgabe 3:<\/p>\r\n\r\n\r\n\r\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0Warum haben Giraffen so einen langen Hals?<\/p>\r\n\r\n\r\n\r\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0Weil der Kopf so weit oben ist.<\/p>\r\n\r\n\r\n\r\n<p>Kommen wir abschlie\u00dfend noch zu ein paar Regeln. Im Quellcode k\u00f6nnte ein Zeilenende durch unterschiedliche Symbole im Text vorkommen: CR (\\u000D), CR-LF (\\u000D\\u000A) oder LF (\\u000A). Der Compiler f\u00fchrt eine sogenannte Normalisierung durch, dass am Ende nur ein LF (\\u000A) im String steht.<\/p>\r\n\r\n\r\n\r\n<p>Textbl\u00f6cke sind keine sogenannten Raw-Strings, die \u201eroh\u201c sind, sondern k\u00f6nnen alle Escape-Sequenzen, unter anderem \\n, \\t, \\&#8216;, \\&#8220; und \\\\, enthalten. So l\u00e4sst sich am Ende immer noch mit \\r eine CR-LF-Sequenz erschaffen:<\/p>\r\n\r\n\r\n\r\n<pre>String joke = \"\"\"<br \/>\u00a0\u00a0\u00a0Lehrer: '76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.'\\r<br \/>\u00a0\u00a0\u00a0Sch\u00fcler: 'Herr Lehrer, so viele sind wir doch gar nicht!'\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>Das Ergebnis w\u00e4re &#8222;Lehrer: &#8217;76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.&#8217;\\u000D\\u000ASch\u00fcler: &#8218;Herr Lehrer, so viele sind wir doch gar nicht!'&#8220;.<\/p>\r\n\r\n\r\n\r\n<p>Kommen im String einfache oder doppelte Anf\u00fchrungszeichen vor, k\u00f6nnen diese ohne Ausmaskierung eingesetzt werden. Ein Sonderfall ist allerdings, wenn das doppelte Anf\u00fchrungszeichen direkt am Ende vor den drei schlie\u00dfenden doppelten Anf\u00fchrungszeichen steht, dann eine Maskierung notwendig:<\/p>\r\n\r\n\r\n\r\n<pre>String joke = \"\"\"<br \/>\u00a0\u00a0\u00a0Lehrer: \"76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.\"<br \/>\u00a0\u00a0\u00a0Sch\u00fcler: \"Herr Lehrer, so viele sind wir doch gar nicht!\\\"\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>Sollen drei Anf\u00fchrungszeichen selbst in der Ausgabe vorkommen, muss eines der drei Anf\u00fchrungszeichen ausmaskiert werden, um nicht f\u00e4lschlicherweise als Abschluss interpretiert zu werden. F\u00fcr die Ausgabe<\/p>\r\n\r\n\r\n\r\n<pre>1 \"<br \/>2 \"\"<br \/>3 \"\"\"<br \/>4 \"\"\"\"<br \/>5 \"\"\"\"\"<br \/>6 \"\"\"\"\"\"<\/pre>\r\n\r\n\r\n\r\n<p>m\u00fcssen wir schreiben:<\/p>\r\n\r\n\r\n\r\n<pre>System.out.println( \"\"\"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a01 \"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a02 \"\"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a03 \"\"\\\"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a04 \"\"\\\"\"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a05 \"\"\\\"\"\"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a06 \"\"\\\"\"\"\\\"\"\"\" );<\/pre>\r\n\r\n\r\n\r\n<p>Solche Zeichenketten sind nicht mehr besonders gut lesbar &#8230;<\/p>\r\n\r\n\r\n\r\n<p>Eine weitere Regel ist, dass Leerzeichen am Ende abgeschnitten werden. Aus dem Textblock<\/p>\r\n\r\n\r\n\r\n<pre>String s = \"\"\"<br \/>1\u2423\u2423\u2423\u2423\u2423\u2423\u2423<br \/>2\u2423\u2423\u2423<br \/>\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>wird der String &#8222;1\\u0002&#8220; &#8212; der \u201cOffene Kasten\u201d \u2423 stehen hier f\u00fcr Leerzeichen, die man sonst im Druck nicht sehen w\u00fcrde.<\/p>\r\n\r\n\r\n\r\n<p>Sollen Leerzeichen am Ende z\u00e4hlen, so f\u00fchrt man die Escape-Sequenz \\s ein. Dies wird \u00fcbersetzt in ein regul\u00e4res Leerzeichen \\u0020. F\u00fcr das obere Beispiel bedeutet das:<\/p>\r\n\r\n\r\n\r\n<pre>String s = \"\"\"<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a01\u00a0 \u00a0 \u00a0 \\s<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a02\u00a0 \\s<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>Leser k\u00f6nnen mit der folgenden Aufgabe ihr Verst\u00e4ndnis pr\u00fcfen: Was f\u00fcr ein String entsteht?<\/p>\r\n\r\n\r\n\r\n<pre>String joke2 = \"\"\"<br \/>\u00a0\u00a0\u00a0Warum haben Giraffen so einen langen Hals? \\<br \/>\u00a0\u00a0\u00a0Weil der Kopf so weit oben ist.<br \/>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"\"\";<\/pre>\r\n\r\n\r\n\r\n<p>Es entsteht &#8222;Warum haben Giraffen so einen langen Hals? Weil der Kopf so weit oben ist.\\n&#8220;.<\/p>\r\n","protected":false},"excerpt":{"rendered":"<p>Strings mit einem Zeilenumbruch kommen in Programmen immer wieder vor, etwa bei Bildschirmausgaben, eingebettetem HTML, XML, JSON oder SQL. Ein Beispiel: String joke =&#8220;Lehrer: &#8217;76 % aller Sch\u00fcler in dieser Klasse haben keine Ahnung von Prozentrechnung.&#8217;\\n&#8220; +\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8222;Sch\u00fcler: &#8218;Herr Lehrer, so viele sind wir doch gar nicht!'&#8220;; Im ersten String-Literal steht \\n f\u00fcr den Zeilenumbruch. Das [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[11],"tags":[],"class_list":["post-4524","post","type-post","status-publish","format-standard","hentry","category-insel"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/4524","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=4524"}],"version-history":[{"count":5,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/4524\/revisions"}],"predecessor-version":[{"id":4567,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/4524\/revisions\/4567"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=4524"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=4524"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=4524"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}