0

What should be a simple piece of code to remove the last character from a string seems to be breaking for no apparent reason (to me anyway).

I am trying to create a simple script to fix colleagues OneDrive file sync problems when they have extra spaces around filenames and extensions.

Coming back to AppleScript after a very long time away and seem to have forgotten everything, just need to understand why what should be simple seems to have me baffled... be gentle with me.

on stripSpaces(thisText)
    local newText
    local pos
    local tempText

    set newText to thisText

    set tempText to ""

    repeat while tempText ≠ newText
        set tempText to newText

        --remove spaces before extension name
        set pos to the offset of ". " in newText

        if pos > 0 then
            set newText to ((characters 1 thru pos of newText) as text) & (characters (pos + 2) thru end of newText) as text
        end if

        --remove spaces before extension
        set pos to the offset of " ." in newText

        if pos > 0 then
            set newText to ((characters 1 thru (pos - 1) of newText) as text) & (characters (pos + 1) thru end of newText) as text
        end if

        --remove leading spaces
        if character 1 of newText = " " then
            set newText to characters 2 thru end of newText as text
        end if

        ---BROKEN SECTION

        --remove trailing spaces
        if (last character of newText) = " " then
            set newText to (characters 1 thru (end of newText) - 1) as text
        end if
    end repeat

    return newText
end stripSpaces

log stripSpaces("   spa     .          nish  . txt  ")

error "Can’t get end of \" spa . nish . txt \"." number -1728 from last insertion point of " spa . nish . txt "

3
  • Question: is the goal here to strip all spaces from file names, or just spaces that are adjacent to period-delimiters? I just want to be clear about the output you're trying to achieve. Commented Jul 9, 2019 at 19:44
  • Ted, it is just to strip out all extra spaces, I know there are RegEx/sed solutions but i’m Intrigued to know how this particular piece of code is broken?.. need to improve my caveman debuggin’ techniques.. Commented Jul 9, 2019 at 21:01
  • That's verbose! For a terse solution consider utilizing Shell Parameter Expansion. For instance; all of the lines of code in the body of your stripSpaces sub-routine could be replaced with the following one line instead: return do shell script "str=" & quoted form of thisText & "; echo \"${str// /}\"" Commented Jul 10, 2019 at 9:03

3 Answers 3

1

I'm a bit late coming in on this. Ted Wrigley has his eye on the ball, noticing, quite rightly, that the neatest and most efficient solution is by way of text item delimiters. I defer credit to him for providing, in my view, the most suitable answer offered thus far, to which this is essentially just a follow-up to illustrate that one can achieve identical results with a much more compact, two-line handler:

to removeAllWhitespace from input as text
    set text item delimiters to {pi, space, tab, linefeed, return}
    return the input's text items as text
end removeAllWhitespace

I also took the liberty of having it clear out all (traditional) whitespace characters besides space, i.e. tab characters, and new-line characters too. You can probably deduce how to remove those options if you so wished.

To call the handler:

removeAllWhitespace from "   spa     .          nish  . txt  "

which outputs: "spa.nish.txt"


Huh? Pi...?

In short: Pick anything random that isn't text and is unlikely to appear in a filename. If in doubt: use pi ^ pi.

In detail: Don't be too bogged down by it being pi: the use of pi in the list of delimiters above holds no significance in and of itself as a quantity, and the only function it serves is as a non-string entity that allows all of the delimiters in the list to be removed from the filename, which will include any occurrences of itself in string-form, i.e. "3.14159265359". Provided this sequence of characters doesn't appear in a filename, pi is likely a good choice to serve as this "dummy delimiter". Otherwise, the key is to swap out pi for any other non-string value. Say... pi + 1 ? I often use null, missing value, or a randomly-generated number. But, if ever you find you can't be bothered to generate a number or think of one yourself, just use pi ^ pi.

Sign up to request clarification or add additional context in comments.

2 Comments

Hi, I think I was a little unclear in stating my problem (which was to fix OneDrive file naming issues, the reason for coding the way I had was to remove spaces from the beginnings and ends of file names (and their extensions) while retaining their inter word spacing. Delimeters were of course the answer to all the other erroneous character problems (Ta Ted). Follow-on problems 1) Renaming fails if the subsequent created new name changes to one already present {span%ish.txt span[ish.txt} 2)Renaming will fail if a higher folder name has already changed. (minor fix... rerun script)
Ah, so in fact, you want " spa . nish . txt " to be renamed to "spa . nish .txt", is that right? Tip for the future: give a series of example inputs and the desired corresponding outputs. It’ll help get you the best answers. After seeing you mark vadian’s answer as the most appropriate solution, it seemed that your objective was different to what you are describing here.
0

It's much easier to get text ranges with text 1 thru -1 of newText where -1 is the last character. This avoids also all as text coercions.

on stripSpaces(thisText)
    local newText
    local pos
    local tempText

    set newText to thisText

    set tempText to ""

    repeat while tempText ≠ newText
        set tempText to newText

        --remove spaces before extension name
        set pos to the offset of ". " in newText

        if pos > 0 then
            set newText to text 1 thru pos of newText & text (pos + 2) thru -1 of newText
        end if

        --remove spaces before extension
        set pos to the offset of " ." in newText

        if pos > 0 then
            set newText to text 1 thru (pos - 1) of newText & text (pos + 1) thru -1 of newText
        end if

        --remove leading spaces
        if first character of newText = space then
            set newText to text 2 thru -1 of newText
        end if

        --remove trailing spaces
        if last character of newText = space then
            set newText to text 1 thru -2 of newText
        end if
    end repeat

    return newText
end stripSpaces

log stripSpaces("   spa     .          nish  . txt  ")

With a little help of AppleScriptObjC the entire handler can be reduced to two lines

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

on stripSpaces(thisText)
    set nsText to current application's NSString's stringWithString:thisText
    return (nsText's stringByReplacingOccurrencesOfString:" " withString:"") as text
end stripSpaces

log stripSpaces("   spa     .          nish  . txt  ")

1 Comment

Whoa! Too early for Master Yoda here... but brilliant.
0

The direct answer to your question is the you shouldn't use 'end' in this context, but use '-1' to indicate the last character, '-2' to indicate the second to last character, etc. The result would end up looking like this:

on stripSpaces(thisText)
    local newText
    local pos
    local tempText

    set newText to thisText

    set tempText to ""

    repeat while tempText ≠ newText
        set tempText to newText

        --remove spaces before extension name
        set pos to the offset of ". " in newText

        if pos > 0 then
            set newText to ((characters 1 thru pos of newText) as text) & (characters (pos + 2) thru -1 of newText) as text
        end if

        --remove spaces before extension
        set pos to the offset of " ." in newText

        if pos > 0 then
            set newText to ((characters 1 thru (pos - 1) of newText) as text) & (characters (pos + 1) thru -1 of newText) as text
        end if

        --remove leading spaces
        if character 1 of newText = " " then
            set newText to characters 2 thru -1 of newText as text
        end if

        ---BROKEN SECTION

        --remove trailing spaces
        if (last character of newText) = " " then
            set newText to (characters 1 thru -2 of newText) as text
        end if
    end repeat

    return newText
end stripSpaces

log stripSpaces("   spa     .          nish  . txt  ")

However, if you goal is simply to strip spaces, you can do that a lot more efficiently using text item delimiters:

on stripSpaces(thisText)
    set tid to my text item delimiters
    set my text item delimiters to " "  -- break text at spaces
    set textParts to text items of thisText
    set my text item delimiters to ""  -- recombine text without spaces
    set strippedText to textParts as text
    set my text item delimiters to tid
    return strippedText
end stripSpaces

log stripSpaces("   spa     .          nish  . txt  ")

1 Comment

Thanks Ted, I will get back to reading that flippin’ manual better... Had not noticed the negative versions of string referencing...

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.