replace text

Hi all,

I have tried to replace the text “CNSC” into “XXXX” in the variable “findText” to match partially with theLOCList but it wont change. If apply item 1 of theLOCList it works.


on findAndReplaceInText(theText, theSearchString, theReplacementString)
	set AppleScript's text item delimiters to theSearchString
	set theTextItems to every text item of theText
	set AppleScript's text item delimiters to theReplacementString
	set theText to theTextItems as string
	set AppleScript's text item delimiters to ""
	return theText
end findAndReplaceInText

set findText to "FA02104K-CNSC-141863_CMYK_"
set theLocList to {"CNSC", "WWGE"}
repeat with i from 1 to (count theLocList)
	set theCurrentListItem to item i of theLocList
	if findText contains item i of theLocList then
		my findAndReplaceInText(findText, theCurrentListItem, "XXXX")
	end if
end repeat

Hi.

In the repeat, you have to set findText to the result from the handler:


on findAndReplaceInText(theText, theSearchString, theReplacementString)
	set AppleScript's text item delimiters to theSearchString
	set theTextItems to every text item of theText
	set AppleScript's text item delimiters to theReplacementString
	set theText to theTextItems as string
	set AppleScript's text item delimiters to ""
	return theText
end findAndReplaceInText

set findText to "FA02104K-CNSC-141863_CMYK_"
set theLocList to {"CNSC", "WWGE"}
repeat with i from 1 to (count theLocList)
	set theCurrentListItem to item i of theLocList
	if findText contains item i of theLocList then
		set findText to my findAndReplaceInText(findText, theCurrentListItem, "XXXX")
	end if
end repeat

return findText

I know this is not what you asked for. If you are one who tries to use as few lines of code as possible, I figure you might be interested in an alternate approach.

property sourceText : "FA02104K-CNSC-141863_CMYK_"
property textToReplace : {"CNSC", "WWGE"}
property replacementText : "XXXX"

set theResult to (do shell script "echo " & sourceText & " | sed -E 's/" & (item 1 of textToReplace) & ¬
	"|" & (item 2 of textToReplace) & "/" & replacementText & "/g'")

OR Better Yet… 1 line of code only

set theResult to (do shell script "echo FA02104K-CNSC-141863_CMYK_ | sed -E 's/CNSC|WWGE/XXXX/g'")

Nigel has answered the OP’s question, and I thought I’d include an ASObjC suggestion FWIW:

use framework "Foundation"
use scripting additions

set theString to "FA02104K-CNSC-141863_CMYK_"
set theString to current application's NSString's stringWithString:theString
set searchText to {"CNSC", "WWGE"}
set replacementText to "XXXX"

repeat with anItem in searchText
	set theString to (theString's stringByReplacingOccurrencesOfString:anItem withString:replacementText)
end repeat

set theNewString to (theString as text)

I ran timing tests and all of the suggestions are reasonably quick.

I guess @Nigel Garvey just didn’t pay attention to the fact that the novice user uses a repeat loop unnecessarily:


on findAndReplaceInText(theText, theSearchString, theReplacementString)
	set AppleScript's text item delimiters to theSearchString
	set theTextItems to every text item of theText
	set AppleScript's text item delimiters to theReplacementString
	set theText to theTextItems as string
	set AppleScript's text item delimiters to ""
	return theText
end findAndReplaceInText

set findText to "FA02104K-CNSC-141863_CMYK_"
set theLocList to {"CNSC", "WWGE"}
my findAndReplaceInText(findText, theLocList, "XXXX") -- no repeat loop

@Nigel Garvey did notice that the repeat’s unnecessary in the context of the snippet posted, but is experienced enough to realise that the code might be an excerpt from some larger script in which the repeat is necessary. Also that novices are more likely to understand — and to be helped and encouraged by — fixes to the code on which they’ve been working themselves and recognise than by something too different. :wink:

Hey Vijay,

I’d streamline that a bit.

-Chris


--------------------------------------------------------
# Auth: Christopher Stone
# dCre: 2021/08/28 19:31
# dMod: 2021/08/28 19:31 
# Appl: Miscellaneous AppleScript
# Task: Find and Replace from a List of Literal Strings.
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @Find, @Replace, @List, @Literal
--------------------------------------------------------

set dataStr to "FA02104K-CNSC-141863_CMYK_"

set theLocList to {"CNSC", "WWGE"}

repeat with textToReplace in theLocList
	if dataStr contains textToReplace then
		set dataStr to my findAndReplaceInText(dataStr, textToReplace, "XXXX")
	end if
end repeat

return dataStr

--------------------------------------------------------
--» HANDLERS
--------------------------------------------------------
on findAndReplaceInText(dataStr, searchStr, replaceStr)
	set {oldTIDS, AppleScript's text item delimiters} to {AppleScript's text item delimiters, searchStr}
	set theTextItems to every text item of dataStr
	set AppleScript's text item delimiters to replaceStr
	set processedText to theTextItems as text
	set AppleScript's text item delimiters to oldTIDS
	return processedText
end findAndReplaceInText
--------------------------------------------------------

For that matter at least for my own use I’d rather employ a regular expression.

Here’s one method using AppleScriptObjC.

-Chris


--------------------------------------------------------
# Auth: Christopher Stone
# dCre: 2021/08/28 20:21
# dMod: 2021/08/28 20:21
# Appl: AppleScriptObjC
# Task: Find-Replace or Change-Text Handler
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @ASObjC, @Find, @Replace, @Text, @RegEx, @cngStr, @Change
--------------------------------------------------------
use AppleScript version "2.4" --» Yosemite or later
use framework "Foundation"
use scripting additions
--------------------------------------------------------

set dataStr to "FA02104K-CNSC-141863_CMYK_"

set newStr to its cngStr:"CNSC|WWGE" intoString:"XXXX" inString:dataStr

--------------------------------------------------------
--» HANDLERS
--------------------------------------------------------
on cngStr:findString intoString:replaceString inString:dataStr
	set anNSString to current application's NSString's stringWithString:dataStr
	set dataStr to (anNSString's ¬
		stringByReplacingOccurrencesOfString:findString withString:replaceString ¬
			options:(current application's NSRegularExpressionSearch) range:{0, length of dataStr}) as text
end cngStr:intoString:inString:
--------------------------------------------------------

@ccstone,

Your AsObjC example deserves a big thanks for the idea. I take it to my library with corrections that are appropriate in my opinion. 1) It seems to me that it is better not to force the user to provide the list in an unusual way. 2) The name of the handler should accurately reflect its functionality.


use framework "Foundation"
use scripting additions

set theString to "FA02104K-CNSC-141863_CMYK_"

my replaceStrings:{"CNSC", "WWGE"} withString:"XXXX" intoString:theString

on replaceStrings:stringsList withString:replaceString intoString:dataStr
	set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "|"}
	set findString to stringsList as text
	set AppleScript's text item delimiters to ATID
	set anNSString to current application's NSString's stringWithString:dataStr
	(anNSString's stringByReplacingOccurrencesOfString:findString withString:replaceString ¬
		options:(current application's NSRegularExpressionSearch) range:{0, length of dataStr}) as text
end replaceStrings:withString:intoString:

NOTE:

  1. I will repeat. The plain AppleScript solution normally doesn’t require repeat loop as well as your AsObjC solution.
  2. This AsObjC solution will fail if the string to search contains “|” :

my replaceStrings:{"CNSC", "WWGE", "CNSC|WWGE"} withString:"XXXX" intoString:"FA02104K-CNSC|WWGE-141863_CMYK_"
--> wrong  result

This is terrible, but I found one case where plain AppleScript code to replace strings can also fail.

This is when the list of substrings contains a substring containing another substring, and it does not precede the contained substring in the list. The solution to the problem should be ordering the list of substrings by the number of their letters.

Following will give wrong result:


on findAndReplaceInText(theText, theSearchString, theReplacementString)
	set AppleScript's text item delimiters to theSearchString
	set theTextItems to every text item of theText
	set AppleScript's text item delimiters to theReplacementString
	set theText to theTextItems as string
	set AppleScript's text item delimiters to ""
	return theText
end findAndReplaceInText

set findText to "FA02104K-CNSC-141863_CMYK_"
set theLocList to {"CNSC", "WWGE", "CNSC-"}
my findAndReplaceInText(findText, theLocList, "XXXX") -- no repeat loop
--> Wrong Result: "FA02104K-XXXX-141863_CMYK_" instead of 
--------------------- "FA02104K-XXXX141863_CMYK_"

Here is the AsObjC solution to the problem:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

on findAndReplaceInText(theText, theSearchStrings, theReplacementString)
	set aList to current application's NSArray's arrayWithArray:theSearchStrings
	set sortedList to aList's sortedArrayUsingDescriptors:{current application's NSSortDescriptor's sortDescriptorWithKey:"length" ascending:false}
	set AppleScript's text item delimiters to (sortedList as list)
	set theTextItems to every text item of theText
	set AppleScript's text item delimiters to theReplacementString
	set theText to theTextItems as string
	set AppleScript's text item delimiters to ""
	return theText
end findAndReplaceInText

set findText to "FA02104K-CNCS|WWGE-CNSC-141863_CMYK_"
set theLocList to {"CNSC", "WWGE", "CNSC-", "CNCS|WWGE"}
my findAndReplaceInText(findText, theLocList, "XXXX") -- no repeat loop
--> now result is correct:        "FA02104K-XXXX-XXXX141863_CMYK_"