I'm trying to make a logging function and everything worked fine until I found this.
This is how print function's interface defined.
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
so if I print print("a\nb"), it prints
a
b
however if I print the same string with the function below
func pLog(_ items: Any...) {
print(items)
}
it prints
["a\nb"]
What am I missing?
Related
I want to be able to override the default Swift standard library print function and include the line number and function name. I have tried several methods such as
public func print(_ items: Any..., separator: String = \(#line), terminator: String = #function {
let output = items.map { "\(terminator) \(separator) -> \($0)" }.joined(separator: " ")
Swift.print(output, terminator: "\n")
}
This correctly prints out the function name of the calling method, however the line number is set to the line number of the print function. I therefore tried changing the separator type to Int and passing #line to it as its default value.
This results in an "ambiguous use of function" error, and changing the signature doesn't help either as the default implementation is called in preference to my function.
I have looked at extensions and even swizzling but neither of these seem fruitful in this instance.
So it seems that in my case a signature change does solve the issue. By being specific about the type passed to the items argument, my custom method is called.
public func print(_ items: String..., filename: String = #file, function : String = #function, line: Int = #line, separator: String = " ", terminator: String = "\n") {
let pretty = "\(URL(fileURLWithPath: filename).lastPathComponent) [#\(line)] \(function)\n\t-> "
let output = items.map { "\($0)" }.joined(separator: separator)
Swift.print(pretty+output, terminator: terminator)
}
Note that all the print statements I care about pass Strings. The default implementation is called otherwise:
print(1+1)
// 2
print("Hello, World!")
// ExampleVC.swift [#101] viewDidLoad()
// -> Hello, World!
print("Hello", "World", separator: ", ", terminator: "!\n")
// ExampleVC.swift [#101] viewDidLoad()
// -> Hello, World!
Sorry if the title is not clear.
What I mean is this:
If I have a variable, we'll call that a, with a value of "Hello\nWorld", it would be written as
var a = "Hello\nWorld
And if I were to print it, I'd get
Hello
World
How could I print it as:
Hello\nWorld
I know this is a little old however I was looking for a solution to the same problem and I figured out something easy.
If you're wanting to print out a string that shows the escape characters like "\nThis Thing\nAlso this"
print(myString.debugDescription)
Here's a more complete version of #Pedro Castilho's answer.
import Foundation
extension String {
static let escapeSequences = [
(original: "\0", escaped: "\\0"),
(original: "\\", escaped: "\\\\"),
(original: "\t", escaped: "\\t"),
(original: "\n", escaped: "\\n"),
(original: "\r", escaped: "\\r"),
(original: "\"", escaped: "\\\""),
(original: "\'", escaped: "\\'"),
]
mutating func literalize() {
self = self.literalized()
}
func literalized() -> String {
return String.escapeSequences.reduce(self) { string, seq in
string.replacingOccurrences(of: seq.original, with: seq.escaped)
}
}
}
let a = "Hello\0\\\t\n\r\"\'World"
print("Original: \(a)\r\n\r\n\r\n")
print("Literalized: \(a.literalized())")
You can't, not without changing the string itself. The \n character sequence only exists in your code as a representation of a newline character, the compiler will change it into an actual newline.
In other words, the issue here is that the "raw" string is the string with the actual newline.
If you want it to appear as an actual \n, you'll need to escape the backslash. (Change it into \\n)
You could also use the following function to automate this:
func literalize(_ string: String) -> String {
return string.replacingOccurrences(of: "\n", with: "\\n")
.replacingOccurrences(of: "\t", with: "\\t")
}
And so on. You can add more replacingOccurrences calls for every escape sequence you want to literalize.
If "Hello\nWorld" is literally the string you're trying to print, then all you do is this:
var str = "Hello\\nWorld"
print(str)
I tested this in the Swift Playgrounds!
Late to the party but the answer to this question is to map the String UnicodeScalarView Unicode.Scalar elements converting them to escaped ascii strings. Then you can simply join back the string:
extension Unicode.Scalar {
var asciiEscaped: String { escaped(asASCII: true) }
}
extension StringProtocol {
var asciiEscaped: String {
unicodeScalars.map(\.asciiEscaped).joined()
}
}
print("Hello\nWorld".asciiEscaped) // Hello\nWorld
Just use double \
var a = "Hello\\nWorld"
Is it possible to extend the functionality of a Swift function? I would like appnd a single character onto every print() function in my program without having to create a brand new function and renaming every instance of print(). Is it possible to create an extension that will append an '*' onto every print instance?
The purpose of this is to create a way of flushing out all of the extra information that XCODE adds into the debugger. I am using print statements to check on the progress and success of different parts of my code, but XCODE fills in thousands of lines of excess info in seconds that quickly cover up my specific statements.
What I want to do:
print("Hello world!")
//Psuedo code:
Extension print(text: String) {
let newText = "*\(text)"
return newText
}
Output:
*Hello World!
I will then filter the Xcode debugging output for asterisks. I have been doing this manually
You can overshadow the print method from the standard library:
public func print(items: Any..., separator: String = " ", terminator: String = "\n") {
let output = items.map { "*\($0)" }.joined(separator: separator)
Swift.print(output, terminator: terminator)
}
Since the original function is in the standard library, its fully qualified name is Swift.print
This code working for me in swift 3
import Foundation
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
let output = items.map { "\($0)" }.joined(separator: separator)
Swift.print(output, terminator: terminator)
}
class YourViewController: UIViewController {
}
If we want to cover all cases with custom print we should create new file for example: CustomPrint.swift and then paste this two methods:
SWIFT 5.1
First (according to ThomasHaz answer)
public func print(_ items: String..., filename: String = #file, function : String = #function, line: Int = #line, separator: String = " ", terminator: String = "\n") {
#if DEBUG
let pretty = "\(URL(fileURLWithPath: filename).lastPathComponent) [#\(line)] \(function)\n\t-> "
let output = items.map { "\($0)" }.joined(separator: separator)
Swift.print(pretty+output, terminator: terminator)
#else
Swift.print("RELEASE MODE")
#endif
}
and second because the first one does't cover dictionary and array printing
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
#if DEBUG
let output = items.map { "\($0)" }.joined(separator: separator)
Swift.print(output, terminator: terminator)
#else
Swift.print("RELEASE MODE")
#endif
}
Enjoy :)
I am trying to get the contents of a string:
var oldString = "hi this \t is my \t string"
oldString = String(oldString.componentsSeparatedByString("\t"))
print(oldString[1])
I get the ERROR : " 'subscript' is unavailable:cannot subscript String
with an Int..."
How would I access a string that is contained in another string?
The only way I have come up with is to get character by character using:
for index in content.characters.indices{
print(String(oldString[index]))
}
The code above results in:
h,
i,
t,
h,
..
I need:
hi this,
is my,
string
Thank you in advance!
You should read the error message and figure out where does the error message come from.
Your String(oldString.componentsSeparatedByString("\t")) gives you a String not [String].
What you need to do is assigning oldString.componentsSeparatedByString("\t") to an array:
let stringArray = oldString.componentsSeparatedByString("\t")
for str in stringArray {
print(str)
}
In swift you can extend any type and add overloads for different operations. In the example below we've created an extension that allows you to subscript String returning each word and get an array from your string.
Simply paste this into a playground to test:
extension String {
func array() -> [String] {
return self.componentsSeparatedByString("\t")
}
subscript (i: Int) -> String {
return self.componentsSeparatedByString("\t")[i]
}
}
Once you've added your extension, you can use it throughout your application like so:
var str = "hi this \t is my \t string"
print(str[0]) //prints hi this
for i in str.array() {
print(i)
}
prints:
hi this
is my
string
var oldString = "hi this \t is my \t string"
let stringArray = oldString.componentsSeparatedByString("\t")
//In case you also need the index while iterating
for (index, value) in stringArray.enumerate(){
print("index is \(index) and string is \(value)")
}
for str in stringArray{
print(str)
}
Output will be as follows
index is 0 and string is hi this
index is 1 and string is is my
index is 2 and string is string
hi this
is my
string
I wrote extension that create split method:
extension String {
func split(splitter: String) -> Array<String> {
return self.componentsSeparatedByString(splitter)
}
}
So in playground I can write:
var str = "Hello, playground"
if str.split(",").count > 1{
var out = str.split(",")[0]
println("output: \(out)") // output: Hello
}
What do I need to make it work with regex like in Java:
str.split("[ ]+")
Because this way it doesn't work.
Thanks,
First, your split function has some redundancy. It is enough to return
return self.componentsSeparatedByString(splitter)
Second, to work with a regular expression you just have to create a NSRegularExpression and then perhaps replace all occurrences with your own "stop string" and finally separate using that. E.g.
extension String {
func split(regex pattern: String) -> [String] {
let template = "-|*~~*~~*|-" /// Any string that isn't contained in the original string (self).
let regex = try? NSRegularExpression(pattern: pattern)
let modifiedString = regex?.stringByReplacingMatches(
in: self,
range: NSRange(
location: 0,
length: count
),
withTemplate: template /// Replace with the template/stop string.
)
/// Split by the replaced string.
return modifiedString?.components(separatedBy: template) ?? []
}
}