{"id":63423,"date":"2007-12-11T01:11:00","date_gmt":"2007-12-11T01:11:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2007\/12\/11\/hey-scripting-guy-how-can-i-replace-text-that-includes-double-quote-marks-and-a-tab-character\/"},"modified":"2007-12-11T01:11:00","modified_gmt":"2007-12-11T01:11:00","slug":"hey-scripting-guy-how-can-i-replace-text-that-includes-double-quote-marks-and-a-tab-character","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-replace-text-that-includes-double-quote-marks-and-a-tab-character\/","title":{"rendered":"Hey, Scripting Guy! How Can I Replace Text That Includes Double Quote Marks and a Tab Character?"},"content":{"rendered":"<p><H2><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" height=\"34\"> <\/H2>\n<P>Hey, Scripting Guy! I read <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/feb05\/hey0208.mspx\"><B>one of your columns<\/B><\/A> on replacing text in a file and I found it very useful. However, I have a somewhat more-complicated need: I want to replace text that includes both double quote marks and a tab character. How can I do that?<BR><BR>&#8212; FC<\/P><IMG border=\"0\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" height=\"5\"><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" height=\"34\"><A href=\"http:\/\/go.microsoft.com\/fwlink\/?linkid=68779&amp;clcid=0x409\"><IMG class=\"farGraphic\" title=\"Script Center\" border=\"0\" alt=\"Script Center\" align=\"right\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/ad.jpg\" width=\"120\" height=\"288\"><\/A> \n<P>Hey, FC. Your know, as anyone who\u2019s ever read <I>Hey, Scripting Guy!<\/I> could tell you, the Scripting Guy who writes this column tends to have a number of opinions that don\u2019t seem to be shared by anyone else in the world. <\/P>\n<P>Take grocery store discount cards, for example. In the US, most grocery store chains now have some sort of discount card: after they ring up your purchase, you swipe your card (or enter your phone number), and, in return, you get a discount of some kind. The upside to this is that you save money on your groceries. The downside? The grocery store chain now has a record of everything you\u2019ve ever bought, and when you bought it. The Scripting Guy who writes this column doesn\u2019t like that; he believes that anything he buys at the grocery store is his own business.<\/P>\n<TABLE id=\"EGD\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P><B>Note<\/B>. Admittedly, he\u2019s also concerned about the fact that, if someone keeps track of something then that something will end up being posted on the Web sooner or later. The Scripting Guy who writes this column has no interest in having every detail of his life made publicly available.<\/P>\n<P>Of course, if he led a more interesting life he might think differently about that.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>At any rate, that\u2019s his moral and philosophical reason for disliking grocery store discount cards. He also has a more practical objection to these cards. For example, this morning the Scripting Guy who writes this column stopped at the store to pick up a couple of doughnuts. The guy in line in front of him (in the only checkout line that was open at the time) had his purchase rung up, then tried swiping his discount card. It didn\u2019t work. He tried swiping it again; it still didn\u2019t work. The grocery store clerk took the card and tried entering the card number by hand. No go. She tried again. Nothing. They then tried entering the man\u2019s phone number. Nothing. At that point the clerk said, \u201cSomething must be wrong with your account. Why don\u2019t you fill out this application and I\u2019ll issue you a brand-new card?\u201d The man grabbed the application and began filling it out. Eventually he completed the application, received a new card, got his discount, and then finally left.<\/P>\n<P>So how long did all that actually take? Let\u2019s put it this way: by the time the Scripting Guy who writes this column had <I>his<\/I> turn at the checkout counter he\u2019d already eaten both the doughnuts.<\/P>\n<P>Although, come to think of it, he has a tendency to do that even when there <I>isn\u2019t<\/I> a delay at the check stand.<\/P>\n<P>Oh, well; easy come, easy go. Fortunately, the Scripting Guys don\u2019t make you sit around for the longest time before they finally get around to taking care of business.<\/P>\n<P>Speaking of which, let\u2019s talk scripting for awhile. As we understand it, FC, you have a file (or maybe a bunch of files) that include this text, where the underscore represents the tab character:<\/P><PRE class=\"codeSample\">rowsep=&#8221;1&#8243;&gt;_\n<\/PRE>\n<P>What you\u2019d like to do is replace each of those values with this:<\/P><PRE class=\"codeSample\">rowsep=&#8221;1&#8243;&gt;&lt;para&gt;\n<\/PRE>\n<P>Unfortunately, though, you\u2019ve run into two problems: the double quotes within the target string, and the tab character at the end of the target string. How can you search for text that includes double quotes and a tab character? Well, here\u2019s one way to do that:<\/P><PRE class=\"codeSample\">Const ForReading = 1\nConst ForWriting = 2<\/p>\n<p>Set objFSO = CreateObject(&#8220;Scripting.FileSystemObject&#8221;)\nSet objFile = objFSO.OpenTextFile(&#8220;C:\\Scripts\\Test.txt&#8221;, ForReading)<\/p>\n<p>strText = objFile.ReadAll\nobjFile.Close<\/p>\n<p>strTargetText = &#8220;rowsep=&#8221; &amp; Chr(34) &amp; &#8220;1&#8221; &amp; Chr(34) &amp; &#8220;&gt;&#8221; &amp; vbTab\nstrReplacementText = &#8220;rowsep=&#8221; &amp; Chr(34) &amp; &#8220;1&#8221; &amp; Chr(34) &amp; &#8220;&gt;&lt;para&gt;&#8221;<\/p>\n<p>strNewText = Replace(strText, strTargetText, strReplacementText)<\/p>\n<p>Set objFile = objFSO.OpenTextFile(&#8220;C:\\Scripts\\Test.txt&#8221;, ForWriting)\nobjFile.WriteLine strNewText\nobjFile.Close\n<\/PRE>\n<P>Let\u2019s see if we can explain what we\u2019re doing here. (Well, as far as the script goes anyway. Explaining what we\u2019re doing here, in a metaphysical sense, goes a bit beyond what we can do in a daily scripting column.) As you can see, we start out in pretty straightforward fashion, defining a pair of constants named ForReading and ForWriting. We\u2019ll use these two constants when we go to open our text file: we\u2019ll use ForReading when we open the file to read in the original contents, then use ForWriting when we reopen the file to overwrite the existing contents with our modified contents.<\/P>\n<TABLE id=\"EFE\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\"><B>Note<\/B>. Yes, it might seem a little silly, but we can\u2019t modify the contents of the file in a single operation. Instead, we need to open the file, read the contents into memory, then close the file. After we make our changes to this virtual copy of the file, we then reopen the file and replace the original contents with our virtual copy.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>After defining our two constants we create an instance of the <B>Scripting.FileSystemObject<\/B>, then use this line of code to open the file C:\\Scripts\\Test.txt for reading:<\/P><PRE class=\"codeSample\">Set objFile = objFSO.OpenTextFile(&#8220;C:\\Scripts\\Test.txt&#8221;, ForReading)\n<\/PRE>\n<P>Once the file is open we use the <B>ReadAll<\/B> method to read in the entire contents of the file, storing those contents in a variable named strText. And then once the file is read we call the <B>Close<\/B> method. (But don\u2019t worry; like we said, we\u2019ll be reopening the file again in due time.)<\/P>\n<P>That brings us to these two lines of code:<\/P><PRE class=\"codeSample\">strTargetText = &#8220;rowsep=&#8221; &amp; Chr(34) &amp; &#8220;1&#8221; &amp; Chr(34) &amp; &#8220;&gt;&#8221; &amp; vbTab\nstrReplacementText = &#8220;rowsep=&#8221; &amp; Chr(34) &amp; &#8220;1&#8221; &amp; Chr(34) &amp; &#8220;&gt;&lt;para&gt;&#8221;\n<\/PRE>\n<P>To tell you the truth, that was our first thought, too. But these lines of code are nowhere near as scary (or as complicated) as they might first look. In line 1, we\u2019re simply defining the text that we want to search for. As you might recall, that happens to be the following, with the underscore representing the tab character:<\/P><PRE class=\"codeSample\">rowsep=&#8221;1&#8243;&gt;_\n<\/PRE>\n<P>As you know by now, FC, working with text like this can be quite \u2026 interesting \u2026. Until you\u2019ve tried it, you might believe that you could assign this value to a variable by using code similar to this:<\/P><PRE class=\"codeSample\">strTargetText = &#8220;rowsep=&#8221;1&#8243;&gt;     &#8221;\n<\/PRE>\n<P>Once you <I>have<\/I> tried it, however, you know better. That\u2019s not going to work, and for two reasons:<\/P>\n<TABLE border=\"0\" cellSpacing=\"0\" cellPadding=\"0\">\n<TBODY>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>The double quotes in the middle of the string create problems. That\u2019s because VBScript uses double quotes to indicate where a string value begins and ends. In this case, and thanks to the double quotes, VBScript thinks our string is this: <B>&#8220;rowsep=&#8221;<\/B>. Because of that, a syntax error occurs when VBScript discovers additional characters beyond the closing quote mark. (Or at least what it <I>thinks<\/I> is a closing quote mark.)<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>Simply pressing the TAB key when assigning a value to a string doesn\u2019t actually assign the tab character to that string; instead, it simply assigns it five blank spaces. Which, though it might look the same, is definitely <I>not<\/I> the same.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<P>In other words, we have to come up with a workaround method for assigning the target text to the variable strTargetText. Our solution involves combining the following string elements:<\/P>\n<TABLE border=\"0\" cellSpacing=\"0\" cellPadding=\"0\">\n<TBODY>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>Beginning double quote marks (&#8220;).<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>The string value r<I>owsep=<\/I>.<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>The function Chr(34). Chr(34) is actually the ASCII representation of the double quote marks. That means we can simply add Chr(34) to our string without having to type actual double quote marks. In turn, that means we can avoid all the problems inherent in having quote marks within quote marks. (And yes, there are other ways we can embed quote marks within quote marks. We just think that this approach is the easiest, especially for beginning script writers.)<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>The string value <I>1<\/I>.<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>Another instance of Chr(34). In other words, another set of double quote marks.<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>The string value<I> &gt;<\/I>.<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>A tab character, indicated by the VBScript constant vbTab. Need to indicate that your string includes a tab character? Don\u2019t do that by pressing the TAB key on the keyboard; do that by using vbTab.<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>Closing double quote marks (&#8220;).<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<P>Or, to put this a little more visually, with the underscore once again representing the tab character, and substituting actual quote marks for Chr(34):<\/P><PRE class=\"codeSample\">&#8221;\n+     rowsep=\n+            &#8221;\n+             1\n+              &#8221;\n+               &gt;\n+                _\n+                 &#8221;\n_____________________<\/p>\n<p>     &#8220;rowsep=&#8221;1&#8243;&gt;_&#8221;\n<\/PRE>\n<P>What do you know? We got exactly what we were looking for, thanks to Chr(34) and vbTab.<\/P>\n<P>After we construct our string we assign the value to a variable named strTargetText. We then use a similar approach to constructing the replacement text:<\/P><PRE class=\"codeSample\">strReplacementText = &#8220;rowsep=&#8221; &amp; Chr(34) &amp; &#8220;1&#8221; &amp; Chr(34) &amp; &#8220;&gt;&lt;para&gt;&#8221;\n<\/PRE>\n<P>The only difference here? Instead of having a tab character at the end we have the string value <I>&lt;para&gt;<\/I>.<\/P>\n<TABLE id=\"EDH\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\"><B>Note<\/B>. Wouldn\u2019t it have been easier to search for all the tab characters and replace them with <I>&lt;para&gt;<\/I>? Yes, but only if we wanted to replace <I>all<\/I> the tab characters. From FC\u2019s email, it sounded like only specific tab characters (i.e., those following <I>rowsep=&#8221;1&#8243;<\/I>) are supposed to be replaced. That\u2019s why we took this somewhat more-complicated (but also more-foolproof) approach.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>Once we\u2019ve defined our target text and our replacement text, doing the actual replacement is no harder than calling the <B>Replace<\/B> function:<\/P><PRE class=\"codeSample\">strNewText = Replace(strText, strTargetText, strReplacementText)\n<\/PRE>\n<P>That command is going to look for instances of the target text within the value of strText, then replace any found instances with the replacement text. After that\u2019s done this modified text file (or at least our virtual text file) will then be assigned to a variable named strNewText.<\/P>\n<P>Pretty easy when you get right down to it, isn\u2019t it?<\/P>\n<P>Of course, as we keep pointing out, all we\u2019ve done so far is modify our virtual copy of the text file. To modify the actual text file itself, we need to execute this block of code:<\/P><PRE class=\"codeSample\">Set objFile = objFSO.OpenTextFile(&#8220;C:\\Scripts\\Test.txt&#8221;, ForWriting)\nobjFile.WriteLine strNewText\nobjFile.Close\n<\/PRE>\n<P>In line 1, we reopen the file C:\\Scripts\\Test.txt, this time for writing. In line 2 we actually do that writing, using the <B>WriteLine<\/B> method to write the value of the variable strNewText (our modified virtual file) to Test.txt. And then, in line 3 we call the Close method to close the file. At that point we are officially done.<\/P>\n<P>And, not incidentally, the target text within Test.txt will have been replaced as well.<\/P>\n<P>Now, where we were? Oh, right: grocery store discount cards. As it turns out, the man who had trouble with his discount card bought $2.50 worth of bagels. Once they finally got his account straightened out the clerk rang up his savings: 10 cents. We all stood in line for a good 10 minutes just so this guy could save 10 cents.<\/P>\n<TABLE id=\"EJAAC\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P><B>Note<\/B>. At first that hardly seemed worth all the time and trouble. On the other hand, suppose you could save 10 cents each day. At the end of the year, you\u2019d have saved $36.50. At the end of a thousand years, you\u2019d save $36,500.<\/P>\n<P>Gee, now that we put it that way, maybe we need to rethink our opposition to grocery store discount cards. After all, how long could a thousand years take?<\/P><\/TD><\/TR><\/TBODY><\/TABLE><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! I read one of your columns on replacing text in a file and I found it very useful. However, I have a somewhat more-complicated need: I want to replace text that includes both double quote marks and a tab character. How can I do that?&#8212; FC Hey, FC. Your know, as anyone [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[40,3,4,21,14,5],"class_list":["post-63423","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-filesystemobject","tag-scripting-guy","tag-scripting-techniques","tag-string-manipulation","tag-text-files","tag-vbscript"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! I read one of your columns on replacing text in a file and I found it very useful. However, I have a somewhat more-complicated need: I want to replace text that includes both double quote marks and a tab character. How can I do that?&#8212; FC Hey, FC. Your know, as anyone [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/63423","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=63423"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/63423\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=63423"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=63423"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=63423"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}