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$
  • $\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 Jun 19 '15 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. Jun 19 '15 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 Jun 19 '15 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 Jun 19 '15 at 21:55
  • $\begingroup$ @Nasser Have you considered using XML templates? $\endgroup$ – C. E. Jun 19 '15 at 21:57
3
$\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$
  • $\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 Jun 26 '15 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 Jun 26 '15 at 7:49
  • $\begingroup$ @Nasser Great. Let me know if you find any issues with it. $\endgroup$ – Simon Rochester Jun 26 '15 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 Jun 26 '15 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$ – Simon Rochester Jun 27 '15 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$
  • $\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 Jun 20 '15 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$ – Simon Rochester Jun 20 '15 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$ – Simon Rochester Jun 20 '15 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 Jun 20 '15 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$ – Simon Rochester Jun 20 '15 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, privacy policy and cookie policy

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