Jump to content
Macro Express Forums

acantor

Members
  • Posts

    1,531
  • Joined

  • Last visited

  • Days Won

    18

Everything posted by acantor

  1. rberq, your bubble sort code helps me understand why I flunked a computer science course. I think I slept through the lessons on sorting! (Or, perhaps I failed because students couldn't access a computer that ran Pascal, and the entire course was about programming in Pascal!) So lacking skills to develop any kind of Pascal program, and in admiration of your example of an array sort using Macro Express , here's my way, using Macro Express to alphabetize email addresses by way of MS-DOS. 🤣 Variable Set String %Emails% to "zzz@abc.com yyy@xyz.com xxx@123.com" // Unsorted email addresses Variable Set String %FileStart% to "c:\tmp\Sort1.txt" // Unsorted email addresses to be saved to this file Variable Set String %FileEnd% to "c:\tmp\Sort2.txt" // Sorted email addresses to be saved in this file Variable Modify String: Save %Emails% to "%FileStart%" Program Launch: "cmd" (Normal) Parameters: // Start a DOS session... Text Type (Simulate Keystrokes): sort %FileStart% > %FileEnd%<ENTER> // Output sort instructions to the command line Text Type (Simulate Keystrokes): exit<ENTER> // Exit the DOS session Delay: 1000 milliseconds Variable Set String set %EmailsSorted% to the contents of %FileEnd% Text Box Display: Sorted Email Messages <VARIABLE SET STRING Option="\x00" Destination="%Emails%" Value="zzz@abc.com\r\nyyy@xyz.com\r\nxxx@123.com" NoEmbeddedVars="FALSE" _COMMENT="Unsorted email addresses"/> <COMMENT/> <VARIABLE SET STRING Option="\x00" Destination="%FileStart%" Value="c:\\tmp\\Sort1.txt" NoEmbeddedVars="FALSE" _COMMENT="Unsorted email addresses to be saved to this file"/> <VARIABLE SET STRING Option="\x00" Destination="%FileEnd%" Value="c:\\tmp\\Sort2.txt" NoEmbeddedVars="FALSE" _COMMENT="Sorted email addresses to be saved in this file"/> <COMMENT/> <VARIABLE MODIFY STRING Option="\x11" Destination="%Emails%" Filename="%FileStart%" Strip="FALSE" NoEmbeddedVars="FALSE"/> <COMMENT/> <PROGRAM LAUNCH Path="cmd" Mode="\x00" Default_Path="TRUE" Wait="1" Get_Console="FALSE" _COMMENT="Start a DOS session..."/> <COMMENT/> <TEXT TYPE Action="0" Text="sort %FileStart% > %FileEnd%<ENTER>" _COMMENT="Output sort instructions to the command line"/> <TEXT TYPE Action="0" Text="exit<ENTER>" _COMMENT="Exit the DOS session"/> <COMMENT/> <DELAY Flags="\x02" Time="1000"/> <VARIABLE SET STRING Option="\x03" Destination="%EmailsSorted%" Filename="%FileEnd%" Strip="FALSE" NoEmbeddedVars="FALSE"/> <COMMENT/> <TEXT BOX DISPLAY Title="Sorted Email Messages" Content="{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil\\fcharset0 Tahoma;}{\\f1\\fnil Tahoma;}}\r\n\\viewkind4\\uc1\\pard\\lang4105\\f0\\fs20 %EmailsSorted%\\lang1033\\f1\\fs14 \r\n\\par }\r\n" Left="821" Top="417" Width="317" Height="375" Monitor="0" OnTop="TRUE" Keep_Focus="TRUE" Mode="\x00" Delay="0"/>
  2. I get it. The fact the size of the array can't be known in advance bugs me. One of my decisions, after I committed to using an array, was to pick a size that will usually work. I chose 999,999! Very kludgey... I don't know how to do that! I may have learned how to sort an array in a computer science course I took about a million years ago, which I failed. My grade was something like 36%! This afternoon, I used my macro "for real" for the first time. I needed to copy email addresses from a horrible and inaccessible web-based email client. I pressed Ctrl + A to select the entire web page and Ctrl + C to copy it to the clipboard. I triggered the macro. It worked!
  3. Here's my solution. It's 20 lines long, but far from bullet-proof. There's no error checking, and the macro seems to get stuck when I feed it a document that contains thousands of lines of text. Oh well. "A small error rate is acceptable!" Assume the text is already in the clipboard. In outline, here's how the macro works: 1. Assign the clipboard to a string variable, %Clip%. 2. Check each character. If the character is valid for an email address, append the character to a string variable, %Result%. If the character is NOT valid, append "*" instead. Example: %Clip% = "abc@def, 123! uvw@xyz" %Result% = "abc@def**123**uvw@xyz" 3. Split %Result% at "*" and assign each value to an array, %PossibleEmail[%Count%]%. From the example above: %PossibleEmail[1]% = "abc@def" %PossibleEmail[2]% = "" %PossibleEmail[3]% = "123" %PossibleEmail[4]% = "" %PossibleEmail[5]% = "uvw@xyz" 4. Check each %PossibleEmail[]% for "@". If it contains the symbol, assume we have an email address, and add it to a list. 5. Display the list: abc@def uvw@xyz Variable Set String %ValidChars% to "abcdefghijklmnopqrstuvwxyz1234567890-_@." // Every valid character in an email address Variable Set String %Clip% from the clipboard contents Variable Set Integer %ClipLength% to the length of variable %Clip% Repeat Start (Repeat %ClipLength% times) // Parse input, one character at a time Variable Modify String: Copy part of text in %Clip% starting at %Count% and 1 characters long to %Char% If Variable %ValidChars% Contains "%Char%" // This character MIGHT be part of an email address Variable Set String %Result% to "%Result%%Char%" // Append the character to %Result% Else // This character cannot be part of an email address Variable Set String %Result% to "%Result%*" // Append "*" to %Result%. It means any invalid character Variable Modify Integer %StarCount%: Increment // Keep track of the number of invalid characters End If End Repeat Variable Modify Integer: %StarCount% = %StarCount% + 1 // Calculate how many times to split %Result% Split String "%Result%" on "*" into %PossibleEmail%, starting at 1 Repeat Start (Repeat %StarCount% times) If Variable %PossibleEmail[%Count%]% Contains "@" // Assume an "@" means a string is part of an email address Variable Set String %EmailList% to "%EmailList% %PossibleEmail[%Count%]%" // Create a list of email addresses End If End Repeat Text Box Display: Scraped Email Addresses <VARIABLE SET STRING Option="\x00" Destination="%ValidChars%" Value="abcdefghijklmnopqrstuvwxyz1234567890-_@." NoEmbeddedVars="FALSE" _COMMENT="Every valid character in an email address"/> <COMMENT/> <VARIABLE SET STRING Option="\x02" Destination="%Clip%" NoEmbeddedVars="FALSE"/> <VARIABLE SET INTEGER Option="\x0D" Destination="%ClipLength%" Text_Variable="%Clip%"/> <COMMENT/> <REPEAT START Start="1" Step="1" Count="%ClipLength%" Save="TRUE" Variable="%Count%" _COMMENT="Parse input, one character at a time"/> <VARIABLE MODIFY STRING Option="\x09" Destination="%Char%" Variable="%Clip%" Start="%Count%" Count="1" NoEmbeddedVars="FALSE"/> <IF VARIABLE Variable="%ValidChars%" Condition="\x06" Value="%Char%" IgnoreCase="TRUE" _COMMENT="This character MIGHT be part of an email address"/> <VARIABLE SET STRING Option="\x00" Destination="%Result%" Value="%Result%%Char%" NoEmbeddedVars="FALSE" _COMMENT="Append the character to %Result%"/> <ELSE _COMMENT="This character cannot be part of an email address"/> <VARIABLE SET STRING Option="\x00" Destination="%Result%" Value="%Result%*" NoEmbeddedVars="FALSE" _COMMENT="Append \"*\" to %Result%. It means any invalid character"/> <VARIABLE MODIFY INTEGER Option="\x07" Destination="%StarCount%" _COMMENT="Keep track of the number of invalid characters"/> <END IF/> <END REPEAT/> <COMMENT/> <VARIABLE MODIFY INTEGER Option="\x00" Destination="%StarCount%" Value1="%StarCount%" Value2="1" _COMMENT="Calculate how many times to split %Result%"/> <COMMENT/> <SPLIT STRING Source="%Result%" SplitChar="*" Dest="%PossibleEmail%" Index="1"/> <REPEAT START Start="1" Step="1" Count="%StarCount%" Save="TRUE" Variable="%Count%"/> <IF VARIABLE Variable="%PossibleEmail[%Count%]%" Condition="\x06" Value="@" IgnoreCase="FALSE" _COMMENT="Assume an \"@\" means a string is part of an email address"/> <VARIABLE SET STRING Option="\x00" Destination="%EmailList%" Value="%EmailList%\r\n%PossibleEmail[%Count%]%" NoEmbeddedVars="FALSE" _COMMENT="Create a list of email addresses"/> <END IF/> <END REPEAT/> <COMMENT/> <TEXT BOX DISPLAY Title="Scraped Email Addresses" Content="{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil\\fcharset0 Tahoma;}{\\f1\\fnil Tahoma;}}\r\n\\viewkind4\\uc1\\pard\\lang4105\\f0\\fs20 %EmailList%\\lang1033\\f1\\fs14 \r\n\\par }\r\n" Left="645" Top="19" Width="664" Height="976" Monitor="0" OnTop="TRUE" Keep_Focus="TRUE" Mode="\x00" Delay="0"/>
  4. Hi Cory, I hadn't realized how complicated the rules for valid email addresses can be. After reading your two posts, I did some reading on the topic. At this point, my macro doesn't handle these situations: A valid email address must contain only one "@". The part before the @ must be 64 characters or less. Underscores are allowed before the @, but not in the domain (after the @). It's not clear, from my limited reading, whether forward slashes, hash marks, plus signs, percent signs, and square brackets are truly valid. It would take less than a minute to modify my script so it will accept the following as a valid email address: postmaster@[123.123.123.123] But it would be a slog to make the macro smart enough to reject this: postmaster@]123.123.123.123[ I should have explicitly said, when setting the challenge, that a small error rate is acceptable!
  5. I'll post my solution -- which I'm still tweaking -- later today or tomorrow. Our approaches to solving this puzzle are quite different! I was intrigued by something you did, which resembles something I tried: Variable Modify String: Replace "@@" in %text% with " " // Replace any double-@, with single space This works nicely for a string like "Hello@@Goodbye". It gets transformed into "Hello Goodbye." Perfect! But what if the string is "Hello@@@Goodbye"? The result is "Hello @Goodbye" -- even if one choose the "Replace All Instances" option. My first inclination was to repeat the instruction: Variable Modify String: Replace "@@" in %text% with " " // Replace any double-@, with single space Variable Modify String: Replace "@@" in %text% with " " // Replace any double-@, with single space That handles "@@@". But what if documents contains long sequences of repeated @-signs, e.g., "Hello@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Goodbye"? How many duplicate instructions should one include? Three? 100? Googolplex? Of course, one could wrap the Replace instruction in a loop to delete repeated characters until they're all gone. But there's something unsatisfying about that. Is there a more elegant way? Or a stronger brute-force method? The RegEx solution is probably trivially simple. Nevertheless, reinventing the wheel can be a valuable learning experience!
  6. You're right. Macro Express is not ideal for this task. But I'm still curious how others will go about solving the problem. For me, it's interesting to find out how far one can go despite Macro Express's constraints. You may be slightly overestimating how much code is involved in solving this puzzle with MEP. My first attempt was about 65 lines long -- including figuring out the word boundaries. Then I tried another way. The script shrunk to about 40 lines, but was too spaghetti like for my liking. My most recent attempt is smaller. A lot smaller. The script probably isn't efficient, but the code to sort out word boundaries is fairly straightforward, even without the undeniable benefits of RegEx. I'm interested! 🤣
  7. I often need to extract email addresses that appear in documents, spreadsheets, email messages, and webpages. I used to do this manually, but recently, I realized I should be using a macro to do the heavy lifting... at least most of the heavy lifting. So here's the challenge: Write a Macro Express script to analyze whatever is in the clipboard, and display only the email addresses it contains. For example, if you copied this to the clipboard: The macro returns this: Although I've set MEP challenges in the past, I don't think I've ever made this a requirement: Make your macro as short as possible, with the fewest number of lines of code as you can. (Comments and blank lines don't count.) I'm still working on this challenge. I have an MEP macro that sort-of works, but it's not a solution I can live with... yet! My suggested rules for this challenge: 1. Your script shouldn't use RegEx. (However, I'd be curious to see how this challenge can be done via RegEx.) 2. Don't worry if your script doesn't handle free-floating at-signs that don't form part of an email address, e.g., "See you @ noon" ... unless you want to! 3. Ensure your script is capable of handling between zero and, say, 1000 email addresses.
  8. Are you using Ctrl + spacebar to select non-contiguous items in File Explorer? If yes, do you navigate to each file by keyboard (Ctrl + arrow keys), or by pointing and clicking? Or are you using the hotkey to toggle between selecting and de-selecting an item without changing focus? You may be dealing with recently-introduced exceptions to the long-established interaction rules in File Explorer. Have you recently changed settings or views in File Explorer?
  9. Although the hotkey is reserved, you can still assign it. MEP will prompt you if you try: So you can assign Ctrl + spacebar, but you'll need to open the script, reset its activation, and click "Yes" when prompted.
  10. Nothing wrong with delays. I frequently insert delays between steps when I'm first building a macro. But as I refine a script, I usually find I can reduce and delete delays. Sometimes the final script has one or two delays. Often there are no delays in the final script. Once in a blue moon, I discover that a delay must be quite long for a macro to work, e.g., a second. But that's rare. Sometimes I deliberately include longish delays because I want to see what's going on as the macro is running.
  11. I don't think it's possible to programmatically incorporate OCR into a Macro Express script. But if you have another application that does the OCR, it may be possible to trigger the OCR from within Macro Express. There are several techniques that could work, e.g., within a Macro Express script, simulate the hotkey that starts the OCR operation. There are limits to what can be accomplished in this way. It might not be easy, or possible, or reliable, for Macro Express and the OCR application to exchange information.
  12. I sometimes clone a macro so that I can test different versions. All macros have the same activation. I test one version several times, disable it, enable another, test it, and so on. Then I delete the ones that work less well. And sometimes I disable macros because I suspect they are causing mischief, e.g., conflicting with a program feature or another macro. Disabling and re-enabling is a quick way to check.
  13. I just downloaded NotepadEx. It's not UWP. Your macro worked, without my modifications. The reason your script didn't work when you tried: There are evil cosmic rays emanating from the brains of extraterrestrials on planet Omega Hyperbolic 4 who are disrupting life on earth.
  14. I agree with Cory. Notepadex might be UWP under Windows 10. I don't have Notepadex, but I had no problem running your macro in other applications on my Windows 10 machine. I simplified your macro. Maybe the four extra instructions are running interference? The date format "hnn" does output the hour and minute, but I'm noticing events are getting logged, which is not usual. I changed the format to "hhmm" (hh = hours, mm = minutes). Date/Time: Type out an adjusted date/time using "yymmdd. hhmm" as the format <DATE/TIME Format="yymmdd. hhmm" Flags="\x32" Date="30-Dec-1899" Day_Offset="0" Month_Offset="0" Year_Offset="0" Hour_Offset="0" Minute_Offset="0" Second_Offset="0" Variable="%T[1]%" IsDateVar="FALSE"/> I can see that your script, at one point, saved the date value to a variable. It's not doing that now. Perhaps rebuild your macro from scratch, without including the unused variable.
  15. The developers of Macro Express are constantly fixing bugs and enhancing features. I'm sure the bugs don't affect everybody, and some people have no use for the enhancements. So if your scripts work and you're able to do what you want to do, upgrading is not 100% necessary. However, when I'm forced to use an older version of Macro Express (as happened recently when I developed scripts for an organization that had standardized on an older version), I'm relieved when I can go back to Version 6.x. The changes may be minor, but newer versions are better overall. For example, when Macro Express Pro introduced the option of setting a string from a list... Variable Set String %Result%: from List I quickly found a way to use it in PowerPoint and Word. The two macros save me dozens of hours of drudgery every year.
  16. One more thing: maybe I'm superstitious, but I store menus, and all commands associated with the menus, in separate .mex files. Macro Express has no difficulty opening multiple .mex files simultaneously. I have two files: one that contains my menu-related scripts; and another file that contains non-menu Macro Express scripts.
  17. I've built menus similar to yours. My experience has been that macros created via the Menu Builder are fragile: they are easily broken. I've seen menu commands change order after exporting and importing the menu. I've also had difficulties when developing menus in one version of Macro Express, but running the menus in another version. The only reliable fix I've found is to rebuild menus, starting from scratch, using the current version of Macro Express. If the previously constructed menus are complex, the rebuild process might be time-consuming. But you'll probably spend far less time rebuilding menus than trying to fix them. The good news is that the menus I rebuilt are working! The up-to-date menus have useful features that were not available in earlier versions of Macro Express, e.g., transparency.
  18. Here's a misdirection I've used -- without giving away the trick: When I need the audience to stop paying attention, I ask if someone might be willing to volunteer. It's how the magician poses the question that produces a few seconds of general distraction. I can't reveal more, lest I be drummed out of the Society of Totally Amateur Magicians!
  19. I imagine that using the Registry is the most elegant way to set a flag that a macro needs to be stopped. Personally, I hesitate using the Windows Registry to store values, although the once or twice I tried to do it, the sun still rose the next morning!
  20. Rberq's method works. The first macro, which runs for about ten seconds, checks for the existence of a specific file each time it loops. That file is Test.txt. (When the first macro begins, it deletes Test.txt.) So the file doesn't exist when the first macro is running uninterrupted. When the second macro begins, the first thing it does is create the file that the first macro monitors. It creates the file by making a copy of a Test.txt backup file. The copy is this: c:\tmp\Test (Copy).txt To set this up, you'll need to create this file. Mine is an empty file. First macro: Delete File/Files: "c:\tmp\test.txt" Delay: 500 milliseconds Text Box Display: First macro Repeat Start (Repeat 10 times) Text Box Update: First macro If File Exists: "c:\tmp\test.txt" Text Box Close: First macro Macro Stop End If Delay: 1000 milliseconds End Repeat <DELETE FILE/FILES Path="c:\\tmp\\test.txt" Progress="FALSE" Recurse="FALSE" Permanent="FALSE"/> <DELAY Flags="\x02" Time="500"/> <COMMENT/> <TEXT BOX DISPLAY Title="First macro" Content="{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil Tahoma;}}\r\n\\viewkind4\\uc1\\pard\\f0\\fs20 %Count%\\fs14 \r\n\\par }\r\n" Left="Center" Top="Center" Width="278" Height="200" Monitor="0" OnTop="TRUE" Keep_Focus="TRUE" Mode="\x01" Delay="0"/> <COMMENT/> <REPEAT START Start="1" Step="1" Count="10" Save="TRUE" Variable="%Count%"/> <TEXT BOX UPDATE Header="First macro" Content="{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil Tahoma;}}\r\n\\viewkind4\\uc1\\pard\\f0\\fs20 %Count%\\fs14 \r\n\\par }\r\n"/> <IF FILE EXISTS File="c:\\tmp\\test.txt" Search_Path="FALSE"/> <TEXT BOX CLOSE Header="First macro"/> <MACRO STOP/> <END IF/> <DELAY Flags="\x02" Time="1000"/> <END REPEAT/> Second macro: Copy File/Files: "c:\tmp\Test (Copy).txt" to "c:\tmp\Test.txt" Delay: 500 milliseconds Text Box Display: Second macro <COPY FILE/FILES Source="c:\\tmp\\Test (Copy).txt" Dest="c:\\tmp\\Test.txt" Progress="FALSE" Recurse="FALSE"/> <COMMENT/> <DELAY Flags="\x02" Time="500"/> <TEXT BOX DISPLAY Title="Second macro" Content="{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang1033{\\fonttbl{\\f0\\fnil Tahoma;}}\r\n\\viewkind4\\uc1\\pard\\f0\\fs14 \r\n\\par }\r\n" Left="Center" Top="Center" Width="278" Height="200" Monitor="0" OnTop="TRUE" Keep_Focus="TRUE" Mode="\x00" Delay="0"/>
  21. Whoever designed the Windows-compatible keyboard must have been a magician. Magicians are experts at distraction and misdirection. The designers placed a key in a conspicuous part of the keyboard, and gave it a memorable label, but nobody notices the key -- even though it's directly in front of our faces, and it's been there, for decades! I studied magic for a while. The first time I performed a mind-reading trick, I did something innocent-looking to distract the audience so that I could quickly glance at the answer I was about to reveal. To my surprise, the audience spent five or more seconds glancing around the room, oblivious to the fact that I had distracted them. I had caused time to disappear from their lives. In that moment I realized that if I continued the study of magic, I might be able to hide the Statue of Liberty via distraction! I realize the above has nothing to do with macros, or with Macro Express!
  22. I'm not sure how I know this. My guess: Around 1996, when I bought my first GUI computer (a PC clone running Windows 95), I didn't have a mouse. Within a few days, I had figured out how to do almost everything via keyboard. Around that time, keyboard makers were releasing "Windows-95 compatible" keyboards; they featured a Windows key (which is equivalent to clicking "Start") and an "application" key (which is the equivalent to right-clicking, or pressing Shift+F10). Maybe I had a dim memory of the application key, and transferred that knowledge to Macro Express? BTW, modern keyboards still have an application key, although I doubt one person in a thousand ever notices it. It's usually on the bottom row, right side, near the Alt and Ctrl keys. But sometimes it's somewhere totally different. I don't think you can assign the application key as a Macro Express hotkey, but you can assign its equivalent, Shift + F10. I recently learned that Macro Express supports assigning the right-mouse button as a hotkey.
  23. My first effort sent keystrokes to activate "Disable" or "Enable" on the "Macro" menu... something like this: Alt+M // Key sequence to activate the "Macro" menu B // Accelerator key to activate Disa_b_le and Ena_b_le But for reasons I don't remember, this macro sometimes failed. My recollection (which may be faulty) is that another menu item sometimes appeared that also used "B" as its accelerator. The clunky workaround: Once the script opened the Macro menu, the script outputted <UP ARROW> six or eight times to reach Disable/Enable. Alt+M // Key sequence to activate the "Macro" menu Up // Navigate to Disable/Enable. (Can't use Down: the order of the menu changes) Up Up Up Up Up etc. Enter The latest incarnation of the macro is simpler. It relies on the fact that Enable/Disable also appears on the context menu when the focused script name is right-clicked. The keyboard shortcut to right-click is Shift+F10. Then "B" toggles Disable and Enable. Then I noticed the "Misc Keys" button in the "Text Type" window: One of the many options is <APP>, which is the equivalent of a right click. So here's the script to Enable/Disable a script: Text Type (Simulate Keystrokes): <APP>b Although this works reliably on my computer, on some systems, you might have to do this: Text Type (Simulate Keystrokes): <APP> Delay: 100 milliseconds Text Type (Simulate Keystrokes): b
  24. I constantly disable and re-enable scripts in the Macro Express Explorer. For example, in the screen below, the top four scripts are enabled, and the last one is disabled. The first four scripts are active, and the last one is inactive. I disable and enable often enough that I realized, at some point, that a hotkey would be the most efficient way to do this. But there is no built-in hotkey. So I used Macro Express to produce my own. I chose Alt + B because B appears in both words, "Disable" and "Enable." But choose whatever hotkey you want! Here are some of the many non-hotkey ways to disable and enable a script: - Choose a command from the "Macro" menu - Right-click the script and choose a command from the context menu - Open a script's "Properties" and check/uncheck a box - Open the script, navigate to the "Miscellaneous" tab, and check/uncheck a box. While experimenting with different methods, I realized some methods work better than others. Some methods work a lot better! Although I think I finally settled on a reliable solution -- which I'll share -- I'm curious how others might crack this puzzle. The reason I'm posing this challenge is because a reliable solution can probably be generalized when automating other tasks in Macro Express Explorer.
×
×
  • Create New...