Thread overview
How to escape control characters?
Mar 31, 2016
cy
Mar 31, 2016
Seb
Mar 31, 2016
H. S. Teoh
Mar 31, 2016
cy
Mar 31, 2016
cy
Aug 23, 2022
Bastiaan Veelo
Aug 23, 2022
Salih Dincer
Aug 24, 2022
Salih Dincer
March 31, 2016
This might be a dumb question. How do I format a string so that all the newlines print as \n and all the tabs as \t and such?
March 31, 2016
On Thursday, 31 March 2016 at 03:15:49 UTC, cy wrote:
> This might be a dumb question. How do I format a string so that all the newlines print as \n and all the tabs as \t and such?

http://dlang.org/spec/lex.html#WysiwygString

r"ab\n" or `ab\n`
March 30, 2016
On Thu, Mar 31, 2016 at 03:23:52AM +0000, Seb via Digitalmars-d-learn wrote:
> On Thursday, 31 March 2016 at 03:15:49 UTC, cy wrote:
> >This might be a dumb question. How do I format a string so that all the newlines print as \n and all the tabs as \t and such?
> 
> http://dlang.org/spec/lex.html#WysiwygString
> 
> r"ab\n" or `ab\n`

Or the verbose way:

	"abc\\ndef\\tghi"

But if the string is only known at runtime, you'd probably have to use std.regex.replaceAll or something like that to manually escape characters. Or implement manual substitution with a pipeline:

	string myString = ...;
	string escapedStr = myString
		.chunks(1)
		.map!(c => (c == "\n") ? "\\n" :
			   (c == "\r") ? "\\r" :
			   (c == "\t") ? "\\t" :
			   c)
		.joiner
		.array;


T

-- 
Береги платье снову, а здоровье смолоду.
March 31, 2016
On Thursday, 31 March 2016 at 03:23:52 UTC, Seb wrote:
> http://dlang.org/spec/lex.html#WysiwygString
>
> r"ab\n" or `ab\n`

Yes I know. But I mean like,

string a = r"ab\n";
writeln(escape(a)); // => ab\n

March 31, 2016
Oh, cool.

On Thursday, 31 March 2016 at 03:29:19 UTC, H. S. Teoh wrote:
> Or implement manual substitution with a pipeline:
> 	string myString = ...;
> 	string escapedStr = myString
> 		.chunks(1)
> 		.map!(c => (c == "\n") ? "\\n" :
> 			   (c == "\r") ? "\\r" :
> 			   (c == "\t") ? "\\t" :
> 			   c)
> 		.joiner
> 		.array;

What I did was

string escapedStr = myString
  .replace("\n",`\n`)
  .replace("\r",`\r`)
  .replace("\t",`\t`);

That makes like 3 copies of the string I guess, but whatever. I'm not sure how efficient a general chunking filter would be on 1-byte chunks, and I certainly don't want to be creating a zillion unescaped 1-byte strings, so if I cared I'd probably do something like this:

auto escapedStr = appender!string;
for(c;myString) {
  switch(c) {
  case '\n':
   escapedStr.put("\\n");
  case '\r':
   escapedStr.put("\\r");
  ...
  default:
   escapedStr.put(c);
  }
}

It'd have to be long and boring to get all the control characters though, and possibly unicode ones too, or do "\xNN" style byte escapes. So I was hoping something standard already existed.
August 23, 2022

On Thursday, 31 March 2016 at 03:15:49 UTC, cy wrote:

>

This might be a dumb question. How do I format a string so that all the newlines print as \n and all the tabs as \t and such?

The easiest is this:

import std.conv;
string str = `Hello "World"
line 2`;
writeln([str].text[2..$-2]); // Hello \"World\"\nline 2

I know this is an old post, but I felt this trick needed to be shared.

This takes advantage of the fact that std.format escapes the characters in an array of strings. So we create an array where str is the only element, and convert that to text. Without the [2..$-2] slicing the output would be ["Hello \"World\"\nline 2"].

A slightly more efficient implementation is

string escape(string s)
{
    import std.array : appender;
    import std.format : FormatSpec, formatValue;

    FormatSpec!char f;
    auto w = appender!string;
    w.reserve(s.length);
    formatValue(w, [s], f);
    return w[][2 .. $ - 2];
}

And the inverse:

string unescape(string s)
{
    import std.format : FormatSpec, unformatValue;

    FormatSpec!char f;
    string str = `["` ~ s ~ `"]`;
    return unformatValue!(string[])(str, f)[0];
}

Perhaps escape() and unescape() should be part of std.format so that they can be refactored to use std.format.internal.write.formatElement directly, eliminating the conversion to array.

-- Bastiaan.

August 23, 2022

On 8/23/22 6:09 AM, Bastiaan Veelo wrote:

>

On Thursday, 31 March 2016 at 03:15:49 UTC, cy wrote:

>

This might be a dumb question. How do I format a string so that all the newlines print as \n and all the tabs as \t and such?

The easiest is this:

import std.conv;
string str = `Hello "World"
line 2`;
writeln([str].text[2..$-2]); // Hello \"World\"\nline 2

I know this is an old post, but I felt this trick needed to be shared.

This takes advantage of the fact that std.format escapes the characters in an array of strings. So we create an array where str is the only element, and convert that to text. Without the [2..$-2] slicing the output would be ["Hello \"World\"\nline 2"].

A slightly more efficient implementation is

string escape(string s)
{
     import std.array : appender;
     import std.format : FormatSpec, formatValue;

     FormatSpec!char f;
     auto w = appender!string;
     w.reserve(s.length);
     formatValue(w, [s], f);
     return w[][2 .. $ - 2];
}

Without allocations. This took me longer than I had hoped it would. It needs the 1-char buffer to avoid sending the surrounding quotes.

struct EscapedString
{
   string[1] str;
   this(string str) @nogc pure nothrow @safe { this.str[0] = str; }
   void toString(Out)(auto ref Out output)
   {
      import std.format;
      import std.range;
      char buf; // 0xff => empty, 0x0, empty, but not first
      void putter(const(char)[] data) {
         if(!data.length) return;
         if(buf != 0xff)
         {
            if(buf)
               put(output, buf);
         }
         else
            // skip first "
            data = data[1 .. $];
         if(!data.length){
            buf = 0;
            return;
         }
         put(output, data[0 .. $-1]);
         buf = data[$-1];
      }
      scope x = &putter;
      formattedWrite(x, "%(%s%)", str[]);
   }
}

It would be nice to expose the escaping functionality from format so this kind of trickery isn't necessary.

-Steve

August 23, 2022

On Tuesday, 23 August 2022 at 13:09:01 UTC, Steven Schveighoffer wrote:

>

Without allocations. This took me longer than I had hoped it would. It needs the 1-char buffer to avoid sending the surrounding quotes.

Surely what Steve does is better. But for some reason I like simple and useful things.

I sometimes use custom string wrapping. Maybe it will be useful for you, for example:

struct String {
  string str;
  alias str this;
  import std.format, std.string;
  void toString(scope void delegate(const(char)[]) sink,
                FormatSpec!char fmt)const
  {
    auto arr = str.lineSplitter;
    if(fmt.spec == 'n') {
      sink.formattedWrite("%-(%s\\n%)", arr);
    } else sink.formattedWrite("%s", str);
  }
} unittest {
  import std.stdio;
  auto str = `this
  is a
  string`;
  String Str; // simple constructor
  Str = str; // direct assign

  Str.writefln!"%n"; // "this\n  is a\n  string"
  Str.writefln!"%s"; /*
this
  is a
  string
*/
}

The nice thing here is that you can change the format specifiers as you wish.

Thank you all...

SDB@79

August 24, 2022

On Tuesday, 23 August 2022 at 23:17:21 UTC, Salih Dincer wrote:

>

The nice thing here is that you can change the format specifiers as you wish.

Actually, both structures could be combined:

struct EscapedString
{
   string[1] str;
   this(string str) @nogc pure nothrow @safe
   {
     this.str[0] = str;
   }

   import std.format, std.range;
   void toString(scope void delegate(const(char)[]) sink,
                 FormatSpec!char fmt)const
   {
     if(fmt.spec == 'e')
     {
       char buf;
       formattedWrite((const(char)[] data) {
         if(!data.length) return;

         if(buf < 0xff) put(sink, buf);
         else data = data[1 .. $];

         if(data.length) {
           put(sink, data[0 .. $-1]);
           buf = data[$-1];
         } else {
           buf = 0;
           return;
         }
       }, "%(%s%)", str[]);
     } else sink.formattedWrite("%s", str[0]);
   }
} unittest {
  import std.array : appender;
  import std.format;

  auto str = EscapedString();
  auto app = appender!string;

  app.formattedWrite("%e", str);
  assert(app.data.length == 0);

  auto results = [`a\tb`, `a\tba\nb`, `a\tba\nba\rb`,
                  `a\tba\nba\rba b`, `a\tba\nba\rba ba\"b`
                 ];

  foreach(i, char c; [9, 10, 13, 32, 34]) {
    const s = "a" ~ c ~ "b";
    app.formattedWrite("%e", EscapedString(s));
    assert(app.data == results[i],
           format("%s, i = %s", app.data, i));
  }
}

SDB@79