Create Address String from CLPlacemark - string

Can anyone suggest a cleaner was to create a address string from a CLPlacemark.
At present I am using this extension
extension CLPlacemark {
func makeAddressString() -> String {
var address = ""
if subThoroughfare != nil { address = address + " " + subThoroughfare! }
if thoroughfare != nil { address = address + " " + thoroughfare! }
if locality != nil { address = address + " " + locality! }
if administrativeArea != nil { address = address + " " + administrativeArea! }
if postalCode != nil { address = address + " " + postalCode! }
if country != nil { address = address + " " + country! }
return address
}
}
All the instance variables are optionals hence the checking for nil, and I want in the same order of street number, to street, etc.

CLPlacemark has a postalAddress property of type CNPostalAddress. You can format that into a locale-aware string using the CNPostalAddressFormatter.
let formatter = CNPostalAddressFormatter()
let addressString = formatter.string(from: placemark.postalAddress)

You can now flatMap on an Array of Optionals in order to filter out nil values (I think this works since Swift 2). Your example is now basically a one-liner (if you delete the line breaks I inserted for readability):
extension CLPlacemark {
func makeAddressString() -> String {
return [subThoroughfare, thoroughfare, locality, administrativeArea, postalCode, country]
.flatMap({ $0 })
.joined(separator: " ")
}
}
You can take this further, and use nested arrays to achieve more complex styles. Here is an example of a German style shortened address (MyStreet 1, 1030 City):
extension CLPlacemark {
var customAddress: String {
get {
return [[thoroughfare, subThoroughfare], [postalCode, locality]]
.map { (subComponents) -> String in
// Combine subcomponents with spaces (e.g. 1030 + City),
subComponents.flatMap({ $0 }).joined(separator: " ")
}
.filter({ return !$0.isEmpty }) // e.g. no street available
.joined(separator: ", ") // e.g. "MyStreet 1" + ", " + "1030 City"
}
}
}

This should work too.
extension CLPlacemark {
func makeAddressString() -> String {
// Unwrapping the optionals using switch statement
switch (self.subThoroughfare, self.thoroughfare, self.locality, self.administrativeArea, self.postalCode, self.country) {
case let (.Some(subThoroughfare), .Some(thoroughfare), .Some(locality), .Some(administrativeArea), .Some(postalCode), .Some(country)):
return "\(subThoroughfare), \(thoroughfare), \(locality), \(administrativeArea), \(postalCode), \(country)"
default:
return ""
}
}
}
Check out this post for reference:
http://natashatherobot.com/swift-unwrap-multiple-optionals/
EDIT - This will only work if none of the optionals are nil, if one is nil the case will not match. Check the reference link to see how you can detect cases where one might be nil.

Related

Is there some way to test my grammar which refer to EAttributes?

I am stuck at one point as I am unable to test my grammar using Junit test cases. Below is my complete grammar.
ExpressionModel:
expression=Expression;
Expression:
Comparison;
Comparison returns Expression:
Primary ({Comparison.left=current} op=("=" | "!=" | ">=" | "<=" | ">" | "<")right=Primary)* ;
Primary returns Expression:
'(' Expression ')' |
{Not} "!" expression=Primary |
Atomic;
Atomic returns Expression:
{IntConstant} value=INT |
{StringConstant} value=STRING |
{BoolConstant} value=('true' | 'false') |
{VariableRef} variable=[ecore::EAttribute|QualifiedName];
QualifiedName:
ID ('.' ID)*;
If i test my code generator for this grammar by launching an eclipse instance, all i have to do is create an ".ecore" file in the src folder and another file for my grammar and i am easily able to access my variable created in ".ecore" file.
What i mean to say is after launching an eclipse instance, i created a "test.ecore" file having a class "vars" and an EAttribute "alpha" which is of EString type and i created another file "testModel.dsl" now i am easily able to access "vars.alpha" inside this file. Can anyone please help me how to perform same steps if i want to test my code generator without launching an eclipse instance. It would be of great help to me.
I am trying to test below test case-->
#RunWith(XtextRunner)
#InjectWith(ExtendedMyDslInjectorProvider)
class ExpressionDSLCompilationTest {
#Inject extension CompilationTestHelper
#Inject extension ParseHelper
#Inject extension ReflectExtensions
#Inject extension IQualifiedNameProvider
#Inject Provider<XtextResourceSet> resourceSetProvider
#Test
def void ReturnVariable() {
val fooPackage = EcoreFactory::eINSTANCE.createEPackage
fooPackage.name = "foo"
fooPackage.nsPrefix = "foo"
fooPackage.nsURI = "http://foo"
val fooClass = EcoreFactory::eINSTANCE.createEClass
fooClass.name = "vars"
fooPackage.EClassifiers.add(fooClass)
val fooattr = EcoreFactory::eINSTANCE.createEAttribute
fooattr.name = "alpha"
fooattr.EType = EcorePackage::eINSTANCE.EString
val resourceset = resourceSetProvider.get
val resource = resourceset.createResource(URI.createURI("hiTest.ecore"))
fooClass.EStructuralFeatures.add(attr)
resource.contents.add(fooPackage)
// val model = '''foo.vars.alpha'''.parse(resourceset)
'''foo.vars.alpha'''.compile [
getCompiledClass.newInstance => [
assertEquals(
"foo.vars.alpha",
it.invoke("generateCodeForExpression")
)
]
]
}
class ExtendedMyDslInjectorProvider extends ExpressionDSLInjectorProvider {
override internalCreateInjector() {
EcoreSupportStandaloneSetup.setup
return super.internalCreateInjector
}
}
I have already followed steps mentioned in https://www.eclipse.org/forums/index.php/t/1081785/
but this is of no use as it is giving me null pointer exception when i run my test case. Any help will be appreciated.
I am adding a piece of my code generator and highlighting the piece where it is giving error. Hope it will be enough.
class ExpressionDSLGenerator extends AbstractGenerator {
#Inject extension IQualifiedNameProvider
/*Generate Java Code with the name of java file as same as that of ".mexpression" file*/
override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
var str = ""
for (e : resource.allContents.toIterable.filter(ExpressionModel)) {
str += e.checkCompileForFunctionsOrExpresssion
}
fsa.generateFile('''«resource.URI.lastSegment.substring(0,resource.URI.lastSegment.indexOf("."))».java''', str)
}
/*Generates the body of .Java File having class and single checkExpression
Method for Expressions and Functions Separately */
def String checkCompileForFunctionsOrExpresssion(ExpressionModel model) {
'''
public class «model.eResource.URI.lastSegment.substring(0,model.eResource.URI.lastSegment.indexOf("."))»{
public «getExpressionReturnType(model)» generateCodeForExpression() {
return «getExpressionReturnBody(model.expression)»;
}
}
'''
}
def getExpressionReturnType(ExpressionModel model) {
/*If expression is not a comparison and just a single variable or value, we must return value's data type*/
if (model.eContents.size < 2) {
model.expression.getValueReturnType
} /* Return boolean since it will be a comparison*/ else {
"boolean"
}
}
// Utility method to get return type of an expression
def getValueReturnType(Expression e) {
if (e.isInt) {
"int"
} else if (e.isString) {
"String"
} else if (e.isVariable) {
e.variableReturnsType
} else {
"boolean"
}
}
// Utility method to set return type on the basis of variable's return type
def getVariableReturnsType(Expression e) {
switch (e) {
VariableRef: {
<part giving error:-->e.variable.EType is coming to be null, hence null pointer exception>**e.variable.EType.name.equals("EString") ? "String" : e.variable.EType.name.equals(
"EInt") ? "int" : "boolean"**</part giving error>
}
}
}
// Utility method to get return body of an expression
def String getExpressionReturnBody(Expression e) {
switch (e) {
VariableRef: {
getVariableReturn(e.variable)
}
IntConstant: {
e.value.intConstantReturn
}
BoolConstant: {
e.value.booleanConstantReturn
}
StringConstant: {
e.value.stringConstantReturn
}
Not: {
e.expression.notExpressionReturn
}
Comparison: {
val left = e.left.getExpressionReturnBody as String
val right = e.right.getExpressionReturnBody as String
if (e.left.isString) {
getStringCompareBody(left, right, e.op)
} else if (e.left.isBoolean) {
getBoolCompareBody(left, right, e.op)
} else if (e.left.isVariable) {
getVariableReturnsBody(e.left, left, right, e.op)
} else {
getOthersCompareBody(left, right, e.op)
}
}
}
}
// return variable's full name
def getVariableReturn(EAttribute e) {
e.fullyQualifiedName + ""
}
// return integer value
def getIntConstantReturn(int value) {
value + ""
}
// return boolean value
def getBooleanConstantReturn(String value) {
Boolean::parseBoolean(value) + ""
}
// return string value
def getStringConstantReturn(String value) {
"\"" + value + "\""
}
// return not value of the given expression
def getNotExpressionReturn(Expression value) {
"!(" + value.getExpressionReturnBody + ")"
}
// Utility method to check if Expression is a String
def isString(Expression e) {
switch (e) {
StringConstant: {
true
}
default: {
false
}
}
}
// Utility method to check if Expression is a boolean
def isBoolean(Expression e) {
switch (e) {
BoolConstant: {
true
}
default: {
false
}
}
}
// Utility method to check if Expression is a variable
def isVariable(Expression e) {
switch (e) {
VariableRef: {
true
}
default: {
false
}
}
}
// return body of comparison expression for string
def getStringCompareBody(String left, String right, String op) {
switch (op) {
case '=': "(" + left + ".equals(" + right + "))"
case '!=': "!(" + left + ".equals(" + right + "))"
default: false + ""
}
}
// return body of comparison expression for boolean
def getBoolCompareBody(String left, String right, String op) {
switch (op) {
case '=': "(" + left + "==" + right + ")"
case '!=': "(" + left + "!=" + right + ")"
default: false + ""
}
}
// return body of comparison expression for other's
def getOthersCompareBody(String left, String right, String op) {
switch (op) {
case '<': "(" + left + "<" + right + ")"
case '>': "(" + left + ">" + right + ")"
case '>=': "(" + left + ">=" + right + ")"
case '<=': "(" + left + "<=" + right + ")"
case '=': "(" + left + "==" + right + ")"
case '!=': "!(" + left + "==" + right + ")"
default: false + ""
}
}
// body for variable type
def getVariableReturnsBody(Expression e, String left, String right, String operator) {
switch (e) {
VariableRef: {
e.variable.EType.name.equals("EString")
? getStringCompareBody(left, right, operator) : e.variable.EType.name.equals(
"EBoolean") ? getBoolCompareBody(left, right, operator) : getOthersCompareBody(left, right,
operator)
}
}
}
}
#Inject extension ValidationTestHelper h
...
val model = '''foo.vars.alpha'''.parse(resourceset)
model.assertNoErrors
which is the adaption of the forum snippet to your context works perfectly fine
if you want to use CompilationTestHelper then you have to customize it for the resourcetset to add the resource there
e.g.
val model = '''foo.vars.alpha'''.parse(resourceset)
model.assertNoErrors
compile(resourceset) [
getCompiledClass.newInstance => [
assertEquals(
"foo.vars.alpha",
it.invoke("generateCodeForExpression")
)
]
]

Using add or push to add items to an array in groovy

I am getting the following error while push/add items to an array in groovy.
$groovy main.groovy
Caught: groovy.lang.MissingMethodException: No signature of method: [LProgressNotes;.push() is applicable for argument types: (ProgressNotes) values: [ProgressNotes#d35dea7]
Possible solutions: sum(), plus(java.util.Collection), plus([Ljava.lang.Object;), plus(java.lang.Object), use([Ljava.lang.Object;), plus(java.lang.Iterable)
groovy.lang.MissingMethodException: No signature of method: [LProgressNotes;.push() is applicable for argument types: (ProgressNotes) values: [ProgressNotes#d35dea7]
Possible solutions: sum(), plus(java.util.Collection), plus([Ljava.lang.Object;), plus(java.lang.Object), use([Ljava.lang.Object;), plus(java.lang.Iterable)
at main$_buildOutNotes_closure2.doCall(main.groovy:82)
at main.buildOutNotes(main.groovy:75)
at main$buildOutNotes.callCurrent(Unknown Source)
at main.run(main.groovy:64)
Here is the function:
def buildOutNotes(incomingNotes, systemNotes) {
ProgressNotes[] outNotes = systemNotes;
//iterate incoming chares
incomingNotes.each { incoming ->
//split the note further
def iNote = splitIncoming(incoming);
//check that the incoming note is in the system note
def foundNotes = systemNotes.findAll { it.ProgressNote == iNote.ProgressNote }
if(!foundNotes){
//add the incoming note to the outNote
outNotes.push(iNote);
}
}
return outNotes;
}
Here are the articles that show push and add use
https://mrhaki.blogspot.com/2015/01/groovy-goodness-pop-and-push-items-in.html
def list = ['Groovy', 'is', 'great!']
list.push('rocks!')
http://docs.groovy-lang.org/next/html/documentation/working-with-collections.html
def list = [5, 6, 7, 8]
emptyList.add(5)
I am building the example code on https://www.tutorialspoint.com/execute_groovy_online.php.
You can view the example here
http://tpcg.io/NGw4szCv
Here is the full code as well:
//package com.javacodegeeks.groovy.date;
//import static java.util.Calendar.*;
//import groovy.json.*;
//import java.util.Properties;
//import java.util.List;
//progress notes object
class ProgressNotes {
def ActionDate
String ActionBy
String Status
String ProgressNote
ProgressNotes(inActionDate, inActionBy, inStatus, inNote){
this.ActionDate = inActionDate
this.ActionBy = inActionBy
this.Status = inStatus
this.ProgressNote = inNote
}
}
//delimiter
String delimiter = "##";
//out notes
ProgressNotes[] outNotes;
//date patterns
def dateInSystemPattern = "yyyy-MM-dd HH:mm:ss";
def dateIncomingPattern = "MM/dd/yyyy hh:mm ";
/************** SAMPLE DATA START ****************/
//incoming note string
String incomingNote = "2019-12-15T01:29:44 User1: December 13 went to pickup the toilet at the wholesaler " +
"then went to site then remove and replace the toilet then found out that there is a " +
"fruit inside the toilet then clean up the site and silicone around the toilet then " +
"throw the old toilet at dumpster." + delimiter +
"2019-12-13T10:43:05 User2: applied 3 bottles of urinal treatment. let sit for an " +
"hour. augered out urinal main. draining excellent. tried augering toilet. object stuck in " +
"toilet. will not come out. Don will replace." + delimiter +
"2019-12-13T09:18:51 user3: PO 508758 - unclog Washroom " +
"Details: " +
"Unclog toilet bowl and urinal in. Room 116.";
//in system notes
ProgressNotes[] systemNotes = [
["2012-01-26T14:52:50", "User1", "DISPATCHED", "reassign to Space Planning to confirm space availability"],
["2012-02-01T12:23:05", "User2", "DISPATCHED", "spoke to requestor and she has a few relocations and POD requirements."],
["2012-02-01T12:23:45", "User3", "DISPATCHED", "Contacted Customer for clarification spreadsheet is forthcoming for this request."],
["2012-02-03T18:45:00", "User1", "DISPATCHED", "Extending date to allow for clean-up of backlog."]
];
/************** SPLIT incomingNote ****************/
def incomingNotes = [];
if (incomingNote != ""){
incomingNotes = incomingNote.split(delimiter);
}
/************** PICK NOTES ****************/
if (!incomingNotes){
//No incoming notes push the system notes out
outNotes = systemNotes;
}
else{
//check and build the outnotes
outNotes = buildOutNotes(incomingNotes, systemNotes);
}
println("OUTNOTES Length: " + outNotes.length)
println(" ");
/************** HELPER METHODS ****************/
def buildOutNotes(incomingNotes, systemNotes) {
ProgressNotes[] outNotes = systemNotes;
//iterate incoming chares
incomingNotes.each { incoming ->
//split the note further
def iNote = splitIncoming(incoming);
//check that the incoming note is in the system note
def foundNotes = systemNotes.findAll { it.ProgressNote == iNote.ProgressNote }
if(!foundNotes){
//add the incoming note to the outNote
outNotes.push(iNote);
}
}
return outNotes;
}
def splitIncoming(incoming){
//date time characters
int dateTimeChars = 20;
def dateAndTimePart = incoming.substring(0,dateTimeChars).trim();
String remainingNote = incoming.substring(dateTimeChars);
String userPart = "";
String notePart = "";
def remainingNotes = remainingNote.split(":");
if(remainingNotes){
userPart = remainingNotes.getAt(0);
notePart = incoming.substring(dateTimeChars+userPart.length()+1).trim();
}
//build the object
def outNote = new ProgressNotes(dateAndTimePart, userPart, "", notePart);
return outNote;
}
You use an array in your code (ProgressNotes[]), not a list (List<ProgressNotes>). Any of the mentioned methods (add and push) does not exist for Java (and thus Groovy) arrays. An array is fixed size, so once initialized, you can't add any new elements to it - you can only replace existing elements. If you try to add a new element to the array, you will get IndexOutOfBoundsException. Just look at this simple example:
String[] list = ["foo", "bar"]
assert list[0] == "foo"
assert list[1] == "bar"
try {
list[2] = "new"
} catch (IndexOutOfBoundsException e) {
println "Caught!"
}
list[1] = "abc"
println list
Output:
Caught!
[foo, abc]
If you want to use List.add() or List.push() (or event groovier leftShift like [] << "elem") you need to use a list instead of an array. Arrays are a good choice if you know the size of the collection is fixed.
//out notes
List<ProgressNotes> outNotes;

Swift - Replacing emojis in a string with whitespace

I have a method that detects urls in a string and returns me both the urls and the ranges where they can be found. Everything works perfectly until there are emojis on the string. For example:
"I'm gonna do this callenge as soon as I can swing again 😂😂😂\n http://youtu.be/SW_d3fGz1hk"
Because of the emojis, the url extracted from the text is http://youtu.be/SW_d3fGz1 instead of http://youtu.be/SW_d3fGz1hk. I figured that the easiest solution was to just replace the emojis on the string with whitespace characters (cause I need the range to be correct for some text styling stuff). Problem is, this is extremely hard to accomplish with Swift (most likely my abilities with the Swift String API is lacking).
I've been trying to do it like this but it seems that I cannot create a string from an array of unicode points:
var emojilessStringWithSubstitution: String {
let emojiRanges = [0x1F601...0x1F64F, 0x2702...0x27B0]
let emojiSet = Set(emojiRanges.flatten())
let codePoints: [UnicodeScalar] = self.unicodeScalars.map {
if emojiSet.contains(Int($0.value)) {
return UnicodeScalar(32)
}
return $0
}
return String(codePoints)
}
Am I approaching this problem the wrong way? Is replacing emojis the best solution here? If so, how can I do it?
Swift 5
Don't use this hardcoded way to detect emojis. In Swift 5 you can do it easily
let inputText = "Some 🖐string 😂😂😂 with 👹👹 👹 emoji 🖐"
let textWithoutEmoij = inputText.unicodeScalars
.filter { !$0.properties.isEmojiPresentation }
.reduce("") { $0 + String($1) }
print(textWithoutEmoij) // Some string with emoji
You can use pattern matching (for emoji patterns) to filter out emoji characters from your String.
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F601)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x2702)...UnicodeScalar(0x27B0)]
return self.unicodeScalars
.filter { ucScalar in !(emojiPatterns.contains{ $0 ~= ucScalar }) }
.reduce("") { $0 + String($1) }
}
}
/* example usage */
let str = "I'm gonna do this callenge as soon as I can swing again 😂😂😂\n http://youtu.be/SW_d3fGz1hk"
print(str.emojilessStringWithSubstitution)
/* I'm gonna do this callenge as soon as I can swing again
http://youtu.be/SW_d3fGz1hk */
Note that the above only makes use of the emoji intervals as presented in your question, and is in no way representative for all emojis, but the method is general and can swiftly be extended by including additional emoji intervals to the emojiPatterns array.
I realize reading your question again that you'd prefer substituting emojis with whitespace characters, rather than removing them (which the above filtering solution does). We can achieve this by replacing the .filter operation above with a conditional return .map operation instead, much like in your question
extension String {
var emojilessStringWithSubstitution: String {
let emojiPatterns = [UnicodeScalar(0x1F600)...UnicodeScalar(0x1F64F),
UnicodeScalar(0x1F300)...UnicodeScalar(0x1F5FF),
UnicodeScalar(0x1F680)...UnicodeScalar(0x1F6FF),
UnicodeScalar(0x2600)...UnicodeScalar(0x26FF),
UnicodeScalar(0x2700)...UnicodeScalar(0x27BF),
UnicodeScalar(0xFE00)...UnicodeScalar(0xFE0F)]
return self.unicodeScalars
.map { ucScalar in
emojiPatterns.contains{ $0 ~= ucScalar } ? UnicodeScalar(32) : ucScalar }
.reduce("") { $0 + String($1) }
}
}
I the above, the existing emoji intervals has been extended, as per your comment to this post (listing these intervals), such that the emoji check is now possibly exhaustive.
Swift 4:
extension String {
func stringByRemovingEmoji() -> String {
return String(self.filter { !$0.isEmoji() })
}
}
extension Character {
fileprivate func isEmoji() -> Bool {
return Character(UnicodeScalar(UInt32(0x1d000))!) <= self && self <= Character(UnicodeScalar(UInt32(0x1f77f))!)
|| Character(UnicodeScalar(UInt32(0x2100))!) <= self && self <= Character(UnicodeScalar(UInt32(0x26ff))!)
}
}
Emojis are classified as symbols by Unicode. Character sets are typically used in searching operations. So we will use Character sets a property that is symbols.
var emojiString = "Hey there 🖐, welcome"
emojiString = emojiString.components(separatedBy: CharacterSet.symbols).joined()
print(emojiString)
Output is
Hey there , welcome
Now observe the emoji is replaced by a white space so there is two white space and we replace it by the following way
emojiString.replacingOccurrences(of: " ", with: " ")
The above method replace parameter of: "two white space" to with: "single white space"
Getting all emoji is more complicated than you would think. For more info on how to figure out which characters are emoji, check out this stackoverflow post or this article.
Building on that information, I would propose to use the extension on Character to more easily let us understand which characters are emoji. Then add a String extension to easily replace found emoji with another character.
extension Character {
var isSimpleEmoji: Bool {
guard let firstProperties = unicodeScalars.first?.properties else {
return false
}
return unicodeScalars.count == 1 &&
(firstProperties.isEmojiPresentation ||
firstProperties.generalCategory == .otherSymbol)
}
var isCombinedIntoEmoji: Bool {
return unicodeScalars.count > 1 &&
unicodeScalars.contains {
$0.properties.isJoinControl ||
$0.properties.isVariationSelector
}
}
var isEmoji: Bool {
return isSimpleEmoji || isCombinedIntoEmoji
}
}
extension String {
func replaceEmoji(with character: Character) -> String {
return String(map { $0.isEmoji ? character : $0 })
}
}
Using it would simply become:
"Some string 😂😂😂 with emoji".replaceEmoji(with: " ")
I found that the solutions given above did not work for certain characters such as 🏋️🏻‍♂️ and 🧰.
To find the emoji ranges, using regex I converted the full list of emoji characters to a file with just hex values. Then I converted them to decimal format and sorted them. Finally, I wrote a script to find the ranges.
Here is the final Swift extension for isEmoji().
extension Character {
func isEmoji() -> Bool {
let emojiRanges = [
(8205, 11093),
(12336, 12953),
(65039, 65039),
(126980, 129685)
]
let codePoint = self.unicodeScalars[self.unicodeScalars.startIndex].value
for emojiRange in emojiRanges {
if codePoint >= emojiRange.0 && codePoint <= emojiRange.1 {
return true
}
}
return false
}
}
For reference, here are the python scripts I wrote to parse the hex strings to integers and then find the ranges.
convert-hex-to-decimal.py
decimals = []
with open('hex.txt') as hexfile:
for line in hexfile:
num = int(line, 16)
if num < 256:
continue
decimals.append(num)
decimals = list(set(decimals))
decimals.sort()
with open('decimal.txt', 'w') as decimalfile:
for decimal in decimals:
decimalfile.write(str(decimal) + "\n")
make-ranges.py
first_line = True
range_start = 0
prev = 0
with open('decimal.txt') as hexfile:
for line in hexfile:
if first_line:
prev = int(line)
range_start = prev
first_line = False
continue
curr = int(line)
if prev + 1000 < curr: # 100 is abitrary to reduce number of ranges
print("(" + str(range_start) + ", " + str(prev) + ")")
range_start = curr
prev = curr
Don't hard-code the range of emojis, use this instead.
func 去除表情符号(字符串:String) -> String {
let 转换为Unicode = 字符串.unicodeScalars//https://developer.apple.com/documentation/swift/string
let 去除表情后的结果 = 转换为Unicode.filter { (item) -> Bool in
let 判断是否表情 = item.properties.isEmoji
return !判断是否表情//是表情就不保留
}
return String(去除表情后的结果)
}

How should I remove all the leading spaces from a string? - swift

I need a way to remove the first character from a string which is a space. I am looking for a method or even an extension for the String type that I can use to cut out a character of a string.
To remove leading and trailing whitespaces:
let trimmedString = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
Swift 3 / Swift 4:
let trimmedString = string.trimmingCharacters(in: .whitespaces)
The correct way when you want to remove all kinds of whitespaces (based on this SO answer) is:
extension String {
var stringByRemovingWhitespaces: String {
let components = componentsSeparatedByCharactersInSet(.whitespaceCharacterSet())
return components.joinWithSeparator("")
}
}
Swift 3.0+ (3.0, 3.1, 3.2, 4.0)
extension String {
func removingWhitespaces() -> String {
return components(separatedBy: .whitespaces).joined()
}
}
EDIT
This answer was posted when the question was about removing all whitespaces, the question was edited to only mention leading whitespaces. If you only want to remove leading whitespaces use the following:
extension String {
func removingLeadingSpaces() -> String {
guard let index = firstIndex(where: { !CharacterSet(charactersIn: String($0)).isSubset(of: .whitespaces) }) else {
return self
}
return String(self[index...])
}
}
This String extension removes all whitespace from a string, not just trailing whitespace ...
extension String {
func replace(string:String, replacement:String) -> String {
return self.replacingOccurrences(of: string, with: replacement, options: NSString.CompareOptions.literal, range: nil)
}
func removeWhitespace() -> String {
return self.replace(string: " ", replacement: "")
}
}
Example:
let string = "The quick brown dog jumps over the foxy lady."
let result = string.removeWhitespace() // Thequickbrowndogjumpsoverthefoxylady.
Swift 3
You can simply use this method to remove all normal spaces in a string (doesn't consider all types of whitespace):
let myString = " Hello World ! "
let formattedString = myString.replacingOccurrences(of: " ", with: "")
The result will be:
HelloWorld!
Swift 4, 4.2 and 5
Remove space from front and end only
let str = " Akbar Code "
let trimmedString = str.trimmingCharacters(in: .whitespacesAndNewlines)
Remove spaces from every where in the string
let stringWithSpaces = " The Akbar khan code "
let stringWithoutSpaces = stringWithSpaces.replacingOccurrences(of: " ", with: "")
You can also use regex.
let trimmedString = myString.stringByReplacingOccurrencesOfString("\\s", withString: "", options: NSStringCompareOptions.RegularExpressionSearch, range: nil)
For Swift 3.0+ see the other answers. This is now a legacy answer for Swift 2.x
As answered above, since you are interested in removing the first character the .stringByTrimmingCharactersInSet() instance method will work nicely:
myString.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
You can also make your own character sets to trim the boundaries of your strings by, ex:
myString.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "<>"))
There is also a built in instance method to deal with removing or replacing substrings called stringByReplacingOccurrencesOfString(target: String, replacement: String). It can remove spaces or any other patterns that occur anywhere in your string
You can specify options and ranges, but don't need to:
myString.stringByReplacingOccurrencesOfString(" ", withString: "")
This is an easy way to remove or replace any repeating pattern of characters in your string, and can be chained, although each time through it has to take another pass through your entire string, decreasing efficiency. So you can do this:
myString.stringByReplacingOccurrencesOfString(" ", withString: "").stringByReplacingOccurrencesOfString(",", withString: "")
...but it will take twice as long.
.stringByReplacingOccurrencesOfString() documentation from Apple site
Chaining these String instance methods can sometimes be very convenient for one off conversions, for example if you want to convert a short NSData blob to a hex string without spaces in one line, you can do this with Swift's built in String interpolation and some trimming and replacing:
("\(myNSDataBlob)").stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: "<>")).stringByReplacingOccurrencesOfString(" ", withString: "")
For swift 3.0
import Foundation
var str = " Hear me calling"
extension String {
var stringByRemovingWhitespaces: String {
return components(separatedBy: .whitespaces).joined()
}
}
str.stringByRemovingWhitespaces // Hearmecalling
Swift 4
The excellent case to use the regex:
" this is wrong contained teee xt "
.replacingOccurrences(of: "^\\s+|\\s+|\\s+$",
with: "",
options: .regularExpression)
// thisiswrongcontainedteeext
If you are wanting to remove spaces from the front (and back) but not the middle, you should use stringByTrimmingCharactersInSet
let dirtyString = " First Word "
let cleanString = dirtyString.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
If you want to remove spaces from anywhere in the string, then you might want to look at stringByReplacing...
I'd use this extension, to be flexible and mimic how other collections do it:
extension String {
func filter(pred: Character -> Bool) -> String {
var res = String()
for c in self.characters {
if pred(c) {
res.append(c)
}
}
return res
}
}
"this is a String".filter { $0 != Character(" ") } // "thisisaString"
Yet another answer, sometimes the input string can contain more than one space between words. If you need to standardize to have only 1 space between words, try this (Swift 4/5)
let inputString = " a very strange text ! "
let validInput = inputString.components(separatedBy:.whitespacesAndNewlines).filter { $0.count > 0 }.joined(separator: " ")
print(validInput) // "a very strange text !"
Try functional programming to remove white spaces:
extension String {
func whiteSpacesRemoved() -> String {
return self.filter { $0 != Character(" ") }
}
}
Hi this might be late but worth trying. This is from a playground file. You can make it a String extension.
This is written in Swift 5.3
Method 1:
var str = "\n \tHello, playground "
if let regexp = try? NSRegularExpression(pattern: "^\\s+", options: NSRegularExpression.Options.caseInsensitive) {
let mstr = NSMutableString(string: str)
regexp.replaceMatches(in: mstr, options: [], range: NSRange(location: 0, length: str.count), withTemplate: "")
str = mstr as String
}
Result: "Hello, playground "
Method 2:
if let c = (str.first { !($0 == " " || $0 == "\t" || $0 == "\n") }) {
if let nonWhiteSpaceIndex = str.firstIndex(of: c) {
str.replaceSubrange(str.startIndex ..< nonWhiteSpaceIndex, with: "")
}
}
Result: "Hello, playground "
Code less do more.
"Hello World".filter({$0 != " "}) // HelloWorld
You can try This as well
let updatedString = searchedText?.stringByReplacingOccurrencesOfString(" ", withString: "-")
extension String {
var removingWhitespaceAndNewLines: String {
return removing(.whitespacesAndNewlines)
}
func removing(_ forbiddenCharacters: CharacterSet) -> String {
return String(unicodeScalars.filter({ !forbiddenCharacters.contains($0) }))
}
}
If anybody remove extra space from string e.g = "This is the demo text remove extra space between the words."
You can use this Function in Swift 4.
func removeSpace(_ string: String) -> String{
var str: String = String(string[string.startIndex])
for (index,value) in string.enumerated(){
if index > 0{
let indexBefore = string.index(before: String.Index.init(encodedOffset: index))
if value == " " && string[indexBefore] == " "{
}else{
str.append(value)
}
}
}
return str
}
and result will be
"This is the demo text remove extra space between the words."
Swift 3 version
//This function trim only white space:
func trim() -> String
{
return self.trimmingCharacters(in: CharacterSet.whitespaces)
}
//This function trim whitespeaces and new line that you enter:
func trimWhiteSpaceAndNewLine() -> String
{
return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
Trimming white spaces in Swift 4
let strFirstName = txtFirstName.text?.trimmingCharacters(in:
CharacterSet.whitespaces)
For me, the following line used to remove white space.
let result = String(yourString.filter {![" ", "\t", "\n"].contains($0)})
string = string.filter ({!" ".contains($0) })
OK, this is old but I came across this issue myself and none of the answers above worked besides removing all white spaces which can be detrimental to the functionality of your app. My issue was like so:
["This", " is", " my", " array", " it is awesome"]
If trimmed all white spaces this would be the output:
["This", "is", "my", "array", "itisawesome"]
So I needed to eliminate the leading spacing and simply switching from:
let array = jsonData.components(separatedBy: ",")
To
let array = jsonData.components(separatedBy: ", ")
Fixed the issue. Hope someone find this useful in the future.
This worked for me in swift 5
var myString = " Kwame Ch ef "
myString = myString.replacingOccurrences(of: " ", with: "")
print(myString)
output: Kwame Chef
For anyone looking for an answer to remove only the leading whitespaces out of a string (as the question title clearly ask), Here's an answer:
Assuming:
let string = " Hello, World! "
To remove all leading whitespaces, use the following code:
var filtered = ""
var isLeading = true
for character in string {
if character.isWhitespace && isLeading {
continue
} else {
isLeading = false
filtered.append(character)
}
}
print(filtered) // "Hello, World! "
I'm sure there's better code than this, but it does the job for me.
Swift 5+
Remove All whitespace from prefix(start) of the string, you can use similar for sufix/end of the string
extension String {
func deletingPrefix(_ prefix: String) -> String {
guard self.hasPrefix(prefix) else { return self }
return String(self.dropFirst(prefix.count))
}
func removeWhitespacePrefix() -> String {
let prefixString = self.prefix(while: { char in
return char == " "
})
return self.deletingPrefix(String(prefixString))
}
}
Really FAST solution:
usage:
let txt = " hello world "
let txt1 = txt.trimStart() // "hello world "
let txt2 = txt.trimEnd() // " hello world"
usage 2:
let txt = "rr rrr rrhello world r r r r r r"
let txt1 = txt.trimStart(["r", " "]) // "hello world r r r r r r"
let txt2 = txt.trimEnd(["r", " "]) // "rr rrr rrhello world"
if you need to remove ALL whitespaces from string:
txt.replace(of: " ", to: "")
public extension String {
func trimStart(_ char: Character) -> String {
return trimStart([char])
}
func trimStart(_ symbols: [Character] = [" ", "\t", "\r", "\n"]) -> String {
var startIndex = 0
for char in self {
if symbols.contains(char) {
startIndex += 1
}
else {
break
}
}
if startIndex == 0 {
return self
}
return String( self.substring(from: startIndex) )
}
func trimEnd(_ char: Character) -> String {
return trimEnd([char])
}
func trimEnd(_ symbols: [Character] = [" ", "\t", "\r", "\n"]) -> String {
var endIndex = self.count - 1
for i in (0...endIndex).reversed() {
if symbols.contains( self[i] ) {
endIndex -= 1
}
else {
break
}
}
if endIndex == self.count {
return self
}
return String( self.substring(to: endIndex + 1) )
}
}
/////////////////////////
/// ACCESS TO CHAR BY INDEX
////////////////////////
extension StringProtocol {
subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
subscript(range: Range<Int>) -> SubSequence {
let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
return self[startIndex..<index(startIndex, offsetBy: range.count)]
}
subscript(range: ClosedRange<Int>) -> SubSequence {
let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
return self[startIndex..<index(startIndex, offsetBy: range.count)]
}
subscript(range: PartialRangeFrom<Int>) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] }
subscript(range: PartialRangeThrough<Int>) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] }
subscript(range: PartialRangeUpTo<Int>) -> SubSequence { self[..<index(startIndex, offsetBy: range.upperBound)] }
}
Technically not an answer to the original question but since many posts here give an answer for removing all whitespace, here is an updated, more concise version:
let stringWithouTAnyWhitespace = string.filter {!$0.isWhitespace}
Swift 3 version of BadmintonCat's answer
extension String {
func replace(_ string:String, replacement:String) -> String {
return self.replacingOccurrences(of: string, with: replacement, options: NSString.CompareOptions.literal, range: nil)
}
func removeWhitespace() -> String {
return self.replace(" ", replacement: "")
}
}
To remove all spaces from the string:
let space_removed_string = (yourstring?.components(separatedBy: " ").joined(separator: ""))!

Validate if a string in NSTextField is a valid IP address OR domain name

I have an NSTextField where I am asking a user to input a string that is either in IPv4 format, or a domain name such as www.example.com. Currently, my code is:
#IBAction func verifyTarget(sender: NSTextFieldCell) {
var txtTarget: NSTextFieldCell = sender
var strRawTarget: String? = txtTarget.stringValue
println("Input: " + strRawTarget!)
var URLTarget: NSURL?
URLTarget = NSURL.URLWithString(strRawTarget)
if URLTarget {
println("URL \(URLTarget) is valid!")
}
else {
println("URL \(strRawTarget) is not valid!")
}
}
Some example output:
Input:
URL is valid!
Input: adsfasdf
URL adsfasdf is valid!
Input: afe12389hfs. . afopadsf
URL afe12389hfs. . afopadsf is not valid!
Input: 192.292.111.3
URL 192.292.111.3 is valid!
Input: 0.a.0.a
URL 0.a.0.a is valid!
Input: %2
URL %2 is not valid!
Input: %20
URL %20 is valid!
Am I doing something wrong?
Check if IP address is IPv4 or IPv6 in Swift
func validateIpAddress(ipToValidate: String) -> Bool {
var sin = sockaddr_in()
var sin6 = sockaddr_in6()
if ipToValidate.withCString({ cstring in inet_pton(AF_INET6, cstring, &sin6.sin6_addr) }) == 1 {
// IPv6 peer.
return true
}
else if ipToValidate.withCString({ cstring in inet_pton(AF_INET, cstring, &sin.sin_addr) }) == 1 {
// IPv4 peer.
return true
}
return false;
}
NSURL.URLWithString evaluates the URL string you pass it based on the criteria for decoding a relative or absolute address as laid out in these not-all-that-readable documents: RFCs 2396, 1738, and 1808. That is to say, what you're hoping to validate is only a small subset of what NSURL can handle. You're better off using a RegEx or two, perhaps from this answer:
#IBAction func verifyTarget(sender: NSTextFieldCell) {
var txtTarget: NSTextFieldCell = sender
var strRawTarget: String? = txtTarget.stringValue
println("Input: " + strRawTarget!)
let validIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
let validHostnameRegex = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"
if strRawTarget == nil {
println("no input!")
} else if strRawTarget!.rangeOfString(validIpAddressRegex, options: .RegularExpressionSearch) {
println("\(strRawTarget) is a valid IP address")
} else if strRawTarget!.rangeOfString(validHostnameRegex, options: .RegularExpressionSearch) {
println("\(strRawTarget) is a valid hostname")
} else {
println("\(strRawTarget) is not valid")
}
}
The code from #Alin in a more compact form:
extension String {
func isIPv4() -> Bool {
var sin = sockaddr_in()
return self.withCString({ cstring in inet_pton(AF_INET, cstring, &sin.sin_addr) }) == 1
}
func isIPv6() -> Bool {
var sin6 = sockaddr_in6()
return self.withCString({ cstring in inet_pton(AF_INET6, cstring, &sin6.sin6_addr) }) == 1
}
func isIpAddress() -> Bool { return self.isIPv6() || self.isIPv4() }
}
Usage:
let ipv6 = "FE80:0000:0000:0000:0202:B3FF:FE1E:8329"
let ipv6Collapsed = "FE80::0202:B3FF:FE1E:8329"
let ipv4 = "19.117.63.126"
ipv6.isIpAddress() //true
ipv6.isIPv6() //true
ipv6.isIPv4() //false
ipv6Collapsed.isIpAddress() //true
ipv6Collapsed.isIPv6() //true
ipv6Collapsed.isIPv4() //false
ipv4.isIpAddress() //true
ipv4.isIPv6() //false
ipv4.isIPv4() //true
The new Network framework has failable initializers for struct IPv4Address and struct IPv6Address which handle the IP address portion very easily. Doing this in IPv6 with a regex is tough with all the shortening rules.
Unfortunately I can't address the domain name part.
Note that Network framework is recent, so it may force you to compile for recent OS versions.
import Network
let tests = ["192.168.4.4","fkjhwojfw","192.168.4.4.4","2620:3","2620::33"]
for test in tests {
if let _ = IPv4Address(test) {
debugPrint("\(test) is valid ipv4 address")
} else if let _ = IPv6Address(test) {
debugPrint("\(test) is valid ipv6 address")
} else {
debugPrint("\(test) is not a valid IP address")
}
}
output:
"192.168.4.4 is valid ipv4 address"
"fkjhwojfw is not a valid IP address"
"192.168.4.4.4 is not a valid IP address"
"2620:3 is not a valid IP address"
"2620::33 is valid ipv6 address"
Updated to Swift 5.1 based on #Nate Cook response.
#IBAction func verifyTarget(sender: NSTextFieldCell) {
guard let strRawTarget = sender.stringValue else { print("no input!"); return }
print("Input: " + strRawTarget)
if strRawTarget.isValidIpAddress {
print("\(strRawTarget) is a valid IP address")
} else if strRawTarget.isValidHostname {
print("\(strRawTarget) is a valid hostname")
} else {
print("\(strRawTarget) is not valid")
}
}
enum Regex {
static let ipAddress = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
static let hostname = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"
}
extension String {
var isValidIpAddress: Bool {
return self.matches(pattern: Regex.ipAddress)
}
var isValidHostname: Bool {
return self.matches(pattern: Regex.hostname)
}
private func matches(pattern: String) -> Bool {
return self.range(of: pattern,
options: .regularExpression,
range: nil,
locale: nil) != nil
}
}
#IBAction func verifyTarget(sender: NSTextFieldCell) -> Bool {
let validIP = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
if ((sender.stringValue.count == 0) || (sender.stringValue!.range(of: validIP, options: .regularExpression) == nil)) {
return false
}
return true
}
Try something like:
private static func validate(ipAddress: String) -> Bool {
return ipAddress.withCString({ cstring in
var addressV6 = sockaddr_in6()
var address = sockaddr_in()
return inet_pton(AF_INET6, cstring, &addressV6.sin6_addr) == 1 // IPv6.
|| inet_pton(AF_INET, cstring, &address.sin_addr) == 1 // IPv4.
});
}
Simply checks if it's accepted by sockaddr_in function as an IPv4 address, or by sockaddr_in6 as IPv6 address.
I wanted to check whether the string is an IP address or not, I found the following solution and it worked fine.
extension String {
/// Property tells whether a string is a valid IP or not
var isValidIpAddress: Bool {
let validIpAddressRegex = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
let predicate = NSPredicate.init(format: "SELF MATCHES %#", validIpAddressRegex)
let matches = predicate.evaluate(with: self)
return matches
}
}
Usage:
string.isValidIpAddress

Resources