2014-10-13 17 views
5

Ich möchte Strings von "input.txt" lesen und nur diejenigen, die kein (Kommentar) -Symbol am Anfang der Zeile haben. Ich schrieb diesen Code:Vergleichen eines Zeichens in einer Rust-Zeichenfolge mit Indizierung

use std::io::{BufRead, BufReader}; 
use std::fs::File; 

fn main() { 
    let file = BufReader::new(File::open("input.txt").unwrap()); 
    let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect(); 
    let mut iter = lines.iter().filter(|&x| x.chars().next() != "#".chars().next()); 
    println!("{}", iter.next().unwrap()); 
} 

Aber diese Linie

|&x| x.chars().next() != "#".chars().next() 

mir schlecht riecht, weil es wie diese |x| x[0] == "#" aussehen kann, und ich kann das zweite Zeichen in der Zeichenfolge nicht überprüfen.

Also, wie kann ich diesen Code umgestalten?

Antwort

9

Rust-Zeichenfolgen werden als eine Folge von Bytes gespeichert, die Zeichen in UTF-8-Codierung darstellen. UTF-8 ist eine Codierung mit variabler Breite, so dass die Byte-Indexierung Sie innerhalb eines Zeichens belassen kann, was offensichtlich unsicher ist. Aber einen Codepunkt nach Index zu erhalten, ist eine O (n) -Operation. Darüber hinaus ist das Indizieren von Codepunkten nicht das, was Sie wirklich tun möchten, da es Codepunkte gibt, die nicht einmal verbundene Zeichen haben, wie Diakritika oder andere Modifikatoren. Das Indizieren von Graphem-Clustern ist dem korrekten Ansatz näher, wird aber normalerweise beim Text-Rendering oder wahrscheinlich bei der Sprachverarbeitung benötigt.

Was ich meine ist, dass das Indizieren einer Zeichenfolge schwer zu definieren ist, und was die meisten Leute normalerweise wollen, ist falsch. Daher bietet Rust keine generische Indexoperation für Strings.

Gelegentlich müssen Sie jedoch Strings indizieren. Zum Beispiel, wenn Sie im Voraus wissen, dass Ihre Zeichenfolge nur ASCII-Zeichen enthält oder wenn Sie mit Binärdaten arbeiten. In diesem Fall liefert Rust natürlich alle notwendigen Mittel.

Zuerst können Sie immer eine Ansicht der zugrunde liegenden Sequenz von Bytes erhalten. &str hat as_bytes() Methode, die &[u8] zurückgibt, ein Stück Bytes, aus dem die Zeichenfolge besteht. Dann können Sie üblichen Indexierungsvorgang verwenden:

x.as_bytes()[0] != b'#' 

Beachten Sie die spezielle Notation: b'#' bedeutet „ASCII-Zeichen # vom Typ u8“, dh es ist ein Byte Zeichenliteral (auch ist beachten Sie, dass Sie nicht "#".chars().next() zu schreiben brauchen Um das Zeichen # zu erhalten, können Sie einfach '#' schreiben - ein einfaches Zeichenliteral. Dies ist jedoch unsicher, da &str UTF-8-codierte Zeichenfolge ist und das erste Zeichen aus mehr als einem Byte bestehen kann.

Die richtige Methode zur Verarbeitung von ASCII-Daten in Rust ist die Verwendung der ascii crate. Sie können von &str zu &AsciiStr mit der as_ascii_str() Methode gehen. Dann können Sie es wie folgt verwenden:

extern crate ascii; 
use ascii::{AsAsciiStr, AsciiChar}; 

// ... 

x.as_ascii_str().unwrap()[0] != AsciiChar::Hash 

Auf diese Weise müssen Sie etwas tippen, aber Sie werden viel mehr Sicherheit im Gegenzug bekommen, weil as_ascii_str() Kontrollen, die Sie nur mit ASCII-Daten arbeiten.

Manchmal möchten Sie jedoch nur mit Binärdaten arbeiten, ohne sie wirklich als Zeichen zu interpretieren, auch wenn die Quelle einige ASCII-Zeichen enthält. Dies kann zum Beispiel passieren, wenn Sie einen Parser für eine Markup-Sprache wie Markdown schreiben. In diesem Fall können Sie die gesamte Eingabe als eine Folge von Bytes behandeln.

use std::io::{Read, BufReader}; 
use std::fs::File; 

fn main() { 
    let mut file = BufReader::new(File::open("/etc/hosts").unwrap()); 
    let mut buf = Vec::new(); 
    file.read_to_end(&mut buf).unwrap(); 
    let mut iter = buf.split(|&c| c == b'\n').filter(|line| line[0] != b'#'); 
    println!("{:?}", iter.next().unwrap()); 
} 
+2

'x [] as_bytes() [0] = b '#'' ist unsicher * in sinnvoller Verwendung nicht *!.Es gefährdet die Speichersicherheit nicht, es beinhaltet keine ungültigen 'char'-Werte, es macht keine funkigen Dinge mit Typen, und es ist nicht besonders wahrscheinlich, etwas Bedeutungsloses zu tun. In UTF-8 bestehen Multi-Byte-Codepunkte ausschließlich aus Bytes> 127 (d. H. Nicht ASCII), so dass die Suche nach einem Byte mit dem Wert 35 eine vollkommen gute Möglichkeit ist, das Auftreten des U + 0023-Codepunkts zu finden. Aber gewährt: Es ist ein schlechter Stil, und das Ablegen in Bytes ist eine schlechte Angewohnheit für andere Textverarbeitungsaufgaben. – delnan

+1

@delnan, OK, in diesem speziellen Fall hast du recht. Unsicher (angesichts des Sinnes, der normalerweise im Zusammenhang mit Rust verwendet wird) ist wahrscheinlich das falsche Wort dafür. Das gleiche für einen anderen Index als 0 zu schreiben, ist jedoch bedeutungslos und inkorrekt, und der Autor der Frage hat ausdrücklich darum gebeten, auch das zweite Zeichen und wahrscheinlich andere zu überprüfen. –

+2

Die Verwendung des Index 1 zum Testen und Überprüfen des zweiten Codepunkts oder Graphemclusters wäre falsch. Aber Byte-Indizierung ist nicht unbedingt falsch. Die Eigenschaften von UTF-8 erlauben Dinge wie Sub-String-Suchen in Bytes ausgedrückt werden. Es ist oft sinnlos, ja (Char-Iteratoren sind normalerweise besser, und viele Algorithmen werden bereits von libstd zur Verfügung gestellt), aber lass uns die Nachricht nicht aufnehmen. – delnan

Verwandte Themen