10
$\begingroup$

I'd like to write the following in plain text .m file in Mathematica, without having to double escape all the Latex commands:

s = ToString["\\documentclass[12pt,titlepage]{article}
\\begin{document}
It is known that $\\sin(0)=" <> ToString[Sin[0]] <> "$
\\end{document}"];

Is there a way to do the above?

reference: How do I make a string literal without having to escape backslashes?

$\endgroup$
9
  • $\begingroup$ @Pickett How to type the solution in plain text? as in .m file? I tried TextCell but not sure I am doing it correctly. Screen shot !Mathematica graphics I do not know how to do the ctrl-9 and ctrl-0 stuff in text file. I use plain text .m files. If you can show how to do it, using my example above, without using notebook interface, just using plain text input, then that will help $\endgroup$
    – Nasser
    Commented Jun 19, 2015 at 21:47
  • $\begingroup$ @Nasser I don't believe you can do that. The InputForm of Jens' solution is TextCell["\\abc\\def\\ghi"]. $\endgroup$
    – C. E.
    Commented Jun 19, 2015 at 21:50
  • $\begingroup$ @Pickett, I am confused. So what should I type for my example above? In the .m file? I do not want to put "\\" in there. I still do not know what the syntax I am supposed to use. $\endgroup$
    – Nasser
    Commented Jun 19, 2015 at 21:53
  • $\begingroup$ As far as I know the syntax you are seeking does not exist. I guess since the Accepted method there is unworkable for you this question may not be a duplicate, but IMHO you should reference that question and simplify your own, then note specifically that you want entry from within a Package. $\endgroup$
    – Mr.Wizard
    Commented Jun 19, 2015 at 21:55
  • $\begingroup$ @Nasser Have you considered using XML templates? $\endgroup$
    – C. E.
    Commented Jun 19, 2015 at 21:57

3 Answers 3

5
$\begingroup$

I had another answer to this question (now located here) that used the global variable $PreRead to convert raw strings to InputForm prior to evaluation. Unfortunately, that method only works when evaluating the package file interactively, as you would a notebook file. It does not work when loading a package with Get, because $PreRead is not applied to the expressions that are read in from the package file before they are evaluated.

On the other hand, if we are just interested in using raw strings in package files that are to be read in, we can easily preprocess the package files to replace raw strings as they are being read.

One method (possibly not for the faint of heart) is to overload Get for designated package files. Suppose we want to implement the Python raw string syntax for any package file whose name ends with _raw.m. In your init.m file, place the following code:

Unprotect[Get];

Get[file_String] /; StringMatchQ[file, "*_raw.m"] && FileType[file] === File :=
  ToExpression@StringReplace[
    Import[file, "Text"], 
    "r\"\"\"" ~~ Shortest[str__] ~~ "\"\"\"" :> ToString@InputForm@str
]

Protect[Get];

Now make a test package file test_raw.m with contents

s = r"""\documentclass[12pt,titlepage]{article}
\begin{document}
It is known that $\sin(0)=""" <> ToString[Sin[0]] <> r"""$
\end{document}"""

Print[s]

Now, at the command line,

$ math -script test_raw.m

returns

\documentclass[12pt,titlepage]{article}
\begin{document}
It is known that $\sin(0)=0$
\end{document}

It also works when using Get["test_raw.m"] or << test_raw.m. The "context" form <<test_raw` doesn't work; to get it to work we'd have to sacrifice the underscore in the file name, which isn't allowed in a context specifier. Then we'd want to lengthen the designator suffix, maybe to rawstring.m, to reduce the chance of other package files accidentally matching the pattern.

$\endgroup$
6
  • $\begingroup$ Very nice! It works very well so far. I also wrote the string to Latex file, and it compiled with pdflatex with no problems. I tested it on this file SetDirectory["G:\\tmp"]; Print[Directory[]] s=r"""\documentclass[12pt,titlepage]{article} \begin{document} \title{title} \date{1/1/2015} The following in known equation $\sin^2(x)+\cos^2(x)=1$ and this is 100% correct. This was written on 1/1/2015 and this is a string "I am a string" and this is \\n which is left as is. \end{document}"""; Print[s] str = OpenWrite["foo.tex"]; WriteString[str,s]; Close[str] thanks. $\endgroup$
    – Nasser
    Commented Jun 26, 2015 at 7:40
  • $\begingroup$ I've been testing it, and so far I see no issues. It also handles embedded strings and everything else like Python's r""". $\endgroup$
    – Nasser
    Commented Jun 26, 2015 at 7:49
  • $\begingroup$ @Nasser Great. Let me know if you find any issues with it. $\endgroup$ Commented Jun 26, 2015 at 9:14
  • $\begingroup$ Would you happen to know why this method does not work when using the syntax math.exe < foo.m and one must use math.exe -script foo.m ? The reason I am asking is that using the first syntax above allows one to see the commands (IN and OUT) on the screen as they are evaluated as discussed in this question which makes it easier to see where the error is. Would be hard to modify your method to make it work with the first case as well? thanks $\endgroup$
    – Nasser
    Commented Jun 26, 2015 at 19:50
  • $\begingroup$ @Nasser It works with math.exe -script foo.m because in script mode the kernel reads the whole input file at once using Get. The syntax math.exe < foo.m is the same as interactive mode, but with input being read in (presumably one line at a time) from foo.m instead of from stdin. If we could find what Mma function is being used to read in the input, we could probably adapt this solution to work, but I haven't been able to yet. Another possibility would be to process the input file with another script and then feed it to the kernel, but so far I haven't found an easy way to do that. $\endgroup$ Commented Jun 27, 2015 at 0:18
4
$\begingroup$

If I'm not mistaken, an approach like this may work for you:

clean = {
   "\t" -> "\\t",
   "\b" -> "\\b",
   "\n" -> "\\n"};

StringReplace["\begin{5}", clean]

If too many exceptions appear it may be easier to set up something with FromCharacterCode.


Interesting, I found that InputForm auto-strings its argument, like a string version of Hold:

In[1]:= InputForm["\t"]

Out[1]//InputForm= "\t"

In[2]:= InputForm["\t"] // ToString // Characters

Out[2]= {"\"", "\\", "t", "\""}

So maybe just this will work for you:

r = StringTrim[ToString[InputForm[#]], "\""] &;

r@"\t"

That this works through the function seems to imply certain things about where escape sequences are processed.

$\endgroup$
6
  • $\begingroup$ The InputForm trick seems to work, even though it produces warnings about "Unknown string escape" characters if I try it with the example in the question. I guess one can just ignore those warnings. $\endgroup$
    – Jens
    Commented Jun 20, 2015 at 6:47
  • 1
    $\begingroup$ I was working on this approach, but gave up, since I don't think it's feasible. A major issue is that any line breaks in the original string are converted to \\n in the output, which is not what's desired. Try it with the example string from the OP. Also, a backslash followed by a space is a commonly used LaTeX command, but Mathematica automatically converts "\ " to " " before anything can be done with it. $\endgroup$ Commented Jun 20, 2015 at 6:48
  • $\begingroup$ If you do want to try this method, you can use Off[Syntax::stresc] to get get rid of the warnings. Also, you'll want to extend the list of replacements to {"\b" -> "\\b", "\f" -> "\\f", "\n" -> "\\n", "\r" -> "\\r", "\t" -> "\\t"}. $\endgroup$ Commented Jun 20, 2015 at 6:50
  • $\begingroup$ @SimonRochester Strange, in my test the line break was correctly appearing as \n. So I think this is still closer to the goal than my approach. But after looking more closely, I don't think it's completely reliable after all. $\endgroup$
    – Jens
    Commented Jun 20, 2015 at 6:52
  • $\begingroup$ @Jens If you just use // InputForm // ToString then the line breaks are fine. But then, for example, you won't convert the LaTeX command "\nu" properly to "\\nu". So you need to add a StringReplace with "\n" -> "\\n", at which point I think the line breaks will be messed up... $\endgroup$ Commented Jun 20, 2015 at 6:58
3
$\begingroup$

There is no direct equivalent to Python's r. So from the comments it's clear that we have to cheat in order to get this formatting to work purely in a text file environment. One obvious way to do it is as follows:

toString[x_]:=StringReplace[x,""->"\\"];
s = toString["documentclass[12pt,titlepage]{article}                                               
begin{document}                                                                                    
It is known that $sin(0)=" <> ToString[Sin[0]] <> "$                                               
end{document}"];
Export["snippet.tex",s,"Text"]

Here, I simply decided to replace the special character \ in the $\LaTeX$ code ("special" because it has catcode 0 in $\TeX$ and at the same time is used in newlines etc., which makes it hard to isolate in a string replacement) by a less special character that is never used for anything else, either in normal text or in $\TeX$. In the example, I chose , but you could for example also choose > or <, if you use standard $\LaTeX$ (because then you'll type \leq etc. instead of these symbols; of course, that would then become <leq).

The point of this exercise is that the single replacement character (such as ) is reasonably easy to type (I have to press Shift-Option-k to get that symbol) and equally easy to recognize as the prefix of a $\LaTeX$ command, compared to the original \. But now we can easily use StringReplace to do the ugly replacement by \\ behind the scenes. If you do it this way, you can choose any replacement character that is not already assigned a meaning either as a letter or a special catcode allowed to appear in $\LaTeX$.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.