str.hasSuffix("str") not working

let str = "Hello, playground"

if str.hasPrefix("Hello") { // true

print("Prefix exists")

}

if str.hasSuffix("ground") { // true

print("Suffix exists")

}

Is working ... but


let key = (paramArr[paramArr.count - 1])

let keyStr:String = key.lowercased()

print("->" + keyStr + "<-")

print(keyStr.hasSuffix(".png"))


PRODUCES:


false

->gardendecorations/snowman/sprites/snowman_body_lower.png<-


keyStr.hasSuffix(".png") should be true


What am I doing wrong?

Could you show what paramArr is.


Is it in playground (that's what I guess) ? In code ?


When I test this in Playground

let key = ("gardendecorations/snowman/sprites/snowman_body_lower.PNG")
let keyStr: String = key.lowercased()
print(keyStr)
print(keyStr.hasSuffix(".png"))


No surprise, it works and yields:.

gardendecorations/snowman/sprites/snowman_body_lower.png

true


So please post the exact and more complete code and the exact console result.

Not something you type in the forum. Probably, you have a typo in playGround that we do not see here (check what you have exactly in hasSuffix() ; may be there is an error in ".png"

The complete code is somewhat more difficult to post.

It is a logging postprocessor for unity.


I run it by piping the result of a unity command line build to a command line tool.


The plain text output from the buidl is similar to, ( this could be simulated by using ls | mycommandlinetool)


512.2 kb 1.3% Built-in Texture2D: SpriteAtlasTexture-OUTFIT_DOCTOR-1024x1024-fmt33

512.2 kb 1.3% Assets/AssetBundleContent/Egg/DressDragon/Garments/MagicianOutfit/magician_longsleevedshirt_texture.png

512.2 kb 1.3% Assets/AssetBundleContent/Egg/DressDragon/Garments/SantaOutfit/santa_shirt_texture_asset.png

512.2 kb 1.3% Assets/AssetBundleContent/Egg/DressDragon/Garments/SantaOutfit/santa_pants_texture_asset.png

512.2 kb 1.3% Assets/AssetBundleContent/Egg/DressDragon/Garments/LuciaOutfit/lucia_skirt_texture_asset.png


I use


while let line = readLine() {


// Line is one row and I split it on space

var paramArr = line.split(separator: " ")

// Key is last parameter of the split

let key = (paramArr[paramArr.count - 1])

//Change it to a lowercased string

let keyStr:String = key.lowercased()

//Print it out to be certain no trailing characers exist

print("->" + keyStr + "<-")

//Print out the test

print(keyStr.hasSuffix(".png"))

}


Output result is

->assets/assetbundlecontent/egg/mailbox/packages/snowplow/snowplow.png<-

false

->assets/assetbundlecontent/egg/mailbox/packages/frog/mailbox_item_frog_croak.wav<-

false

->assets/assetbundlecontent/egg/gardendecorations/snowcannon/sprites/snow_cannon_tip_asset.png<-

false

Could you test several suffix and tell what you get:


        print(keyStr.hasSuffix("g"))
        print(keyStr.hasSuffix("ng"))
        print(keyStr.hasSuffix("png"))
        print(keyStr.hasSuffix(".png"))

Could you check also


let end = keyStr.suffix(4)
for u in end.utf16 {
    print(u)
}

and check if you get

46

112

110

103

Change the line `print("->" + keyStr + "<-")` to `debugPrint("->" + keyStr + "<-")` and check what you get.

That is a clue. I probably will be able to find the misstake myself now. The sequence is not what expected and, it remains constant independent of the content of keyStr.

27

91

48

109


Thank you, it was most helpful.

Had


"->assets/assetbundlecontent/egg/mailbox/packages/doll/doll.asset\u{1B}[0m<-"


Tryed all kinds of trimmingCharacters with now effect.

So, you have

esc[0m


Not at all .png


May be this added after the .png ?


try to extract a longer suffix to see if you find the sequence 46 112 110 103 inside

let end = keyStr.suffix(12) // strings are long enough, they have at least 12 in suffix
for u in end.utf16 { 
    print(u) 
}



I would guess they are just befor the esc, or not at all (maybe they skipped the extension)

Seems the code contains an ESC sequence. Usually this happens only whent the output of a command line tool is connected to a terminal.

Have you done something strange to gather these texts? For example, haven't you take it from the buffer of a terminal app?


Anyway, you should better find how you can get plain text from the command line tool.


Until you find some clue to get the right output, you may need to remove ESC sequences by yourself, but it is not easy. There are so many ESC sequences. (en.wikipedia.org/wiki/ANSI_escape_code) No simple trimming would work.


This will removed some limited ESC sequences including the one in your sample line.

let keyStr = "assets/assetbundlecontent/egg/mailbox/packages/doll/doll.asset\u{1B}[0m"
let escSeqPattern = "\u{1B}\\[[0-9]+(?:;[0-9]+)?[A-Za-z]"
let keyStrEscRemoved = keyStr.replacingOccurrences(of: escSeqPattern, with: "", options: .regularExpression)
debugPrint(keyStrEscRemoved)
print(keyStrEscRemoved.hasSuffix(".asset")) //->true


You may need to modify `escSeqPattern` when you find other ESC sequences...

Attempting some tool chaining, and couldn't find a good example for pipes except to use "while let line = readLine()".


These answers helped me to get "forward", but I think there must be better solutions for tool chaining.


Thank you for the help.

You aren't really "tool chaining" here.


The output you are parsing:

512.2 kb 1.3% Assets/AssetBundleContent/Egg/DressDragon/Garments/MagicianOutfit/magician_longsleevedshirt_texture.png


is meant to be human-readable. Those escape characters are likely ANSI terminal sequences to display the text in a colour, boldface, or something even funkier. This is closer to "scraping" than "chaining".


You have a couple of options:

1) See if the command you are running has any different options for this progress output. This looks like progress output, so this is likely all you will get. In other cases, you might have an option for machine-readable output like XML. Sometimes, the authors of the tool don't do the chaining correctly (Yes, I'm talking about you, Apple) and this option won't be useable.

2) Just strip out any control characters with brute force. Make sure to test with non-English text and/or paths. (and whitespace!)

3) Depending on the tool, you may need to do something funky with pseudo terminals. Let's hope you won't need that. You seem to be using Swift anyway. Spawning a process with a pseudo-terminal is deep black-magic in C.


Since I've taken an opportunity to take a dig at Apple, I'll tell you what Apple says to me. NEVER, EVER, PARSE TOOL OUTPUT! There. You've been warned. Apple does have a point. Tool developers (namely Apple) like to change the behaviour of tools, especially when it involves human-readable output. You are not an end-user anymore. Don't you dare click any "update" button without having thoroughly tested everything for a few weeks using beta and newly released versions. This applies to any update, no matter how small.

Pipes (or tool chaining as I think that is the correct name), is common in the unix world.


like:

ls | grep


To do this in c is far more complicated than "while let line = readLine()", it requires also to handle the flow of data, so that the receiving tool isn't flooded. As I haven't done this since I programmed in c for about thirty years ago, I really don't recall how to write them even in c.


As I am learning swift and I found an example of how to make a command line tool, I thought this path has lots of synergy. But there aren't any examples of piping with swift (or rather I couldn't find any).


As of the "log" that I am piping to my command line tool, it isn't made for it. It is as you say a terminal output and the escape characters most probably are colour coding for the terminal. So there apple is correct, I should not make a command line tool to receive the log, but as I can change the tool in seconds every time I use it, apple is wrong (or a tiny bit wrong atleast), as my tool saved me hours of manual log analyzing.

To do this in c is far more complicated than "while let line = readLine()", it requires also to handle the flow of data, so that the receiving tool isn't flooded.

While there are some gotchas associated with pipes, the statement above is unnecessarily alarmist. If you’re building a command-line tool that you intend to use as a filter in a pipe chain, it’s fine to read data with

readline
and write data with
print
. Similarly, if your command-line tool acts as the end of a chain, it’s fine to read data using
readline
.

I tried to explain this in my reply on your other thread. Did you see that?

So there apple is correct …

That’s arguable. Most command line tools that generate escaping sequences (for colour coding and so on) only do that if

stdout
is connected to a terminal, reverting to plain text when
stdout
is connected to a file, a pipe, and so on. If you’re working with a tool that doesn’t do that, I think it’s reasonable for you to a file a bug requesting that.

Of course you need to file the bug with the right folks. If this is a third-party tool then there’s no point filing a bug with Apple about it.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Yes, I am aware of pipes. What I am saying is that the "pipe" concept is only applicable if the tools were designed for that. You can still do a "pipe" operation, but you'll have to carefully parse the data. That is why I say it is closer to scraping. You are using the data in a manner in which it was not intended. You can still do that, but you have to be extra careful in your parsing.


However, with a pipe, one thing you don't have to worry about is flow contol. You don't even need to do readline operations. You can (and should) read until end of file, however you want to do that.


If the goal of this tool is to save you time in analysis, the Swift really isn't appropriate. You will be spending more time than necessary writing the tool. Much of what you learn about Swift in the process will not be applicable to most real-world swift projects.


Someone in your other thread suggested Python. That is an excellent idea.

str.hasSuffix("str") not working
 
 
Q