(नोट: मैं MacOS Sierra 10.12.3 के साथ Xcode 8.2.1 पर स्विफ्ट 3.0.1 का उपयोग कर रहा हूं)
मैंने यहाँ देखे गए सभी उत्तरों को याद किया कि वह LF या CRLF की तलाश में था। यदि सबकुछ ठीक हो जाता है, तो वह सिर्फ एलएफ पर मैच कर सकता है और अंत में अतिरिक्त सीआर के लिए लौटे स्ट्रिंग की जांच कर सकता है। लेकिन सामान्य क्वेरी में कई खोज स्ट्रिंग शामिल हैं। दूसरे शब्दों में, परिसीमनकर्ता को एक होना चाहिए Set<String>
, जहां सेट न तो खाली है और न ही एक स्ट्रिंग के बजाय खाली स्ट्रिंग है।
पिछले साल इस पर मेरी पहली कोशिश में, मैंने "सही काम" करने और तारों के एक सामान्य सेट की खोज करने की कोशिश की। यह बहुत कठिन था; आपको एक पूर्ण विकसित पार्सर और राज्य मशीनों और इस तरह की आवश्यकता है। मैंने इसे छोड़ दिया और इस परियोजना का हिस्सा था।
अब मैं फिर से प्रोजेक्ट कर रहा हूं, और फिर से उसी चुनौती का सामना कर रहा हूं। अब मैं CR और LF पर हार्ड-कोड सर्च करने जा रहा हूं। मुझे नहीं लगता कि किसी को सीआर / एलएफ पार्सिंग के बाहर इस तरह के दो स्वतंत्र और अर्ध-निर्भर पात्रों पर खोज करने की आवश्यकता होगी।
मैं खोज तरीकों का उपयोग कर रहा हूँ Data
, इसलिए मैं यहां स्ट्रिंग एनकोडिंग और सामान नहीं कर रहा हूं। बस कच्चे बाइनरी प्रसंस्करण। बस मान लीजिए कि मुझे एएससीआईआई सुपरसेट मिला, जैसे आईएसओ लैटिन -1 या यूटीएफ -8, यहां। आप अगली-उच्च परत पर स्ट्रिंग एन्कोडिंग को संभाल सकते हैं, और आप इस बात पर ध्यान केंद्रित करते हैं कि क्या माध्यमिक कोड-पॉइंट्स के साथ एक सीआर / एलएफ अभी भी सीआर या एलएफ के रूप में गिना जाता है।
एल्गोरिथ्म: बस अपने वर्तमान बाइट ऑफसेट से अगले सीआर और अगले एलएफ के लिए खोज जारी रखें ।
- यदि न तो पाया जाता है, तो अगले डेटा स्ट्रिंग को वर्तमान ऑफ़सेट से लेकर एंड-ऑफ़-डेटा तक माना जाता है। ध्यान दें कि टर्मिनेटर की लंबाई 0. है। इसे अपने रीडिंग लूप के अंत के रूप में चिह्नित करें।
- यदि कोई LF पहले पाया जाता है, या केवल एक LF पाया जाता है, तो वर्तमान ऑफ़सेट से LF तक के अगले डेटा स्ट्रिंग पर विचार करें। ध्यान दें कि टर्मिनेटर की लंबाई 1. एलएफ के बाद ऑफसेट को स्थानांतरित करें।
- यदि केवल एक सीआर पाया जाता है, तो एलएफ मामले की तरह करें (बस एक अलग बाइट मान के साथ)।
- अन्यथा, हमें एक सीआर मिला, जिसके बाद एक एलएफ था।
- यदि दो समीप हैं, तो एलएफ मामले की तरह संभाल लें, इसके अलावा टर्मिनेटर की लंबाई 2 होगी।
- यदि उनके बीच एक बाइट है, और कहा कि बाइट भी सीआर है, तो हमें "विंडोज डेवलपर ने एक पाठ बाइनरी \ n लिखा है जबकि पाठ मोड में, एक \ r \ r \ n" समस्या दे रहा है। इसके अलावा इसे LF केस की तरह संभालें, इसके अलावा टर्मिनेटर की लंबाई 3 होगी।
- अन्यथा CR और LF जुड़े हुए नहीं हैं, और जस्ट-CR केस की तरह हैंडल करते हैं।
इसके लिए यहां कुछ कोड दिया गया है:
struct DataInternetLineIterator: IteratorProtocol {
typealias LineLocation = (offset: Int, length: Int, terminatorLength: Int)
static let cr: UInt8 = 13
static let crData = Data(repeating: cr, count: 1)
static let lf: UInt8 = 10
static let lfData = Data(repeating: lf, count: 1)
let data: Data
private var lineStartOffset: Int = 0
init(data: Data) {
self.data = data
}
mutating func next() -> LineLocation? {
guard self.data.count - self.lineStartOffset > 0 else { return nil }
let nextCR = self.data.range(of: DataInternetLineIterator.crData, options: [], in: lineStartOffset..<self.data.count)?.lowerBound
let nextLF = self.data.range(of: DataInternetLineIterator.lfData, options: [], in: lineStartOffset..<self.data.count)?.lowerBound
var location: LineLocation = (self.lineStartOffset, -self.lineStartOffset, 0)
let lineEndOffset: Int
switch (nextCR, nextLF) {
case (nil, nil):
lineEndOffset = self.data.count
case (nil, let offsetLf):
lineEndOffset = offsetLf!
location.terminatorLength = 1
case (let offsetCr, nil):
lineEndOffset = offsetCr!
location.terminatorLength = 1
default:
lineEndOffset = min(nextLF!, nextCR!)
if nextLF! < nextCR! {
location.terminatorLength = 1
} else {
switch nextLF! - nextCR! {
case 2 where self.data[nextCR! + 1] == DataInternetLineIterator.cr:
location.terminatorLength += 1
fallthrough
case 1:
location.terminatorLength += 1
fallthrough
default:
location.terminatorLength += 1
}
}
}
self.lineStartOffset = lineEndOffset + location.terminatorLength
location.length += self.lineStartOffset
return location
}
}
बेशक, अगर आपके पास एक Data
लंबाई का एक ब्लॉक है जो कम से कम एक गीगाबाइट का एक महत्वपूर्ण अंश है, तो आप तब भी एक हिट लेंगे जब वर्तमान बाइट ऑफसेट से अधिक सीआर या एलएफ मौजूद नहीं होगा; हमेशा हर पुनरावृत्ति के दौरान अंत तक फलहीन खोज। विखंडू में डेटा पढ़ने से मदद मिलेगी:
struct DataBlockIterator: IteratorProtocol {
let data: Data
private(set) var blockOffset = 0
private(set) var bytesRemaining: Int
let blockSize: Int
init(data: Data, blockSize: Int) {
precondition(blockSize > 0)
self.data = data
self.bytesRemaining = data.count
self.blockSize = blockSize
}
mutating func next() -> Data? {
guard bytesRemaining > 0 else { return nil }
defer { blockOffset += blockSize ; bytesRemaining -= blockSize }
return data.subdata(in: blockOffset..<(blockOffset + min(bytesRemaining, blockSize)))
}
}
आपको इन विचारों को स्वयं एक साथ मिलाना होगा, क्योंकि मैंने अभी तक ऐसा नहीं किया है। विचार करें:
- बेशक, आपको लाइनों को पूरी तरह से एक चंक में निहित पर विचार करना होगा।
- लेकिन आपको संभालना होगा जब एक पंक्ति के छोर आसन्न चोंच में होते हैं।
- या जब समापन बिंदु के बीच कम से कम एक हिस्सा है
- बड़ी जटिलता तब होती है जब लाइन एक मल्टी-बाइट अनुक्रम के साथ समाप्त होती है, लेकिन कहा जाता है कि अनुक्रम दो विखंडू को काटता है! (बस सीआर में समाप्त होने वाली एक पंक्ति जो कि चंक में अंतिम बाइट भी है, एक समतुल्य मामला है, क्योंकि आपको यह देखने के लिए अगली बार पढ़ने की आवश्यकता है कि क्या आपका जस्ट-सीआर वास्तव में एक CRLF या CR-CRLF है। जब समान शेंग्नि होते हैं। सीआर-सीआर के साथ चंक समाप्त होता है।)
- और आपको यह संभालने की जरूरत है कि आपके वर्तमान ऑफसेट से अधिक टर्मिनेटर नहीं हैं, लेकिन एंड-ऑफ-डेटा बाद के चंक में है।
सौभाग्य!