Wie kann ich eine (UTF-8-codierte) Textdatei auf eine bestimmte Anzahl von Zeichen kürzen? Ich interessiere mich nicht für Linienlängen und der Schnitt kann in der Mitte des Wortes sein.
cut
scheint auf Zeilen zu funktionieren, aber ich möchte eine ganze Datei.head -c
Verwendet Bytes, keine Zeichen.
text-processing
Pitel
quelle
quelle
cut
immer noch keine Mehrbyte-Zeichen unterstützt. Wenn es so wäre, könntest du es tuncut -zc-1234 | tr -d '\0'
.Antworten:
Einige Systeme verfügen über einen
truncate
Befehl, mit dem Dateien auf eine bestimmte Anzahl von Bytes (keine Zeichen) gekürzt werden .Ich kenne keine, die auf eine Reihe von Zeichen gekürzt sind, obwohl Sie auf die zurückgreifen könnten,
perl
die auf den meisten Systemen standardmäßig installiert ist:perl
Mit verwenden
-Mopen=locale
wir die Vorstellung des Gebietsschemas, was Zeichen sind (in Gebietsschemas, die den UTF-8-Zeichensatz verwenden, sind das UTF-8-codierte Zeichen). Ersetzen Sie durch,-CS
wenn I / O unabhängig vom Zeichensatz des Gebietsschemas in UTF-8 decodiert / codiert werden soll.$/ = \1234
: Wir setzen das Datensatztrennzeichen auf einen Verweis auf eine Ganzzahl, mit der Datensätze mit fester Länge (in Zeichenanzahl ) angegeben werden können.Wenn wir dann den ersten Datensatz lesen, kürzen wir stdin an der richtigen Stelle (also am Ende des ersten Datensatzes) und beenden ihn.
GNU sed
Mit GNU
sed
können Sie Folgendes tun (vorausgesetzt, die Datei enthält keine NUL-Zeichen oder Folgen von Bytes, die keine gültigen Zeichen bilden - beides sollte für Textdateien gelten):Dies ist jedoch weitaus weniger effizient, da die Datei vollständig gelesen, vollständig im Speicher abgelegt und eine neue Kopie geschrieben wird.
GNU awk
Gleiche mit GNU
awk
:-e code -E /dev/null "$file"
Dies ist eine Möglichkeit, beliebige Dateinamen an zu übergebengawk
RS='^$'
: Schlürfen Modus .Shell eingebaut
Mit
ksh93
,bash
oderzsh
(mit Muscheln ausgenommenzsh
, den Inhalt unter der Annahme nicht NUL enthalten Bytes):Mit
zsh
:Oder:
Mit
ksh93
oderbash
(Vorsicht, es ist falsch für Multi-Byte-Zeichen in mehreren Versionen vonbash
):ksh93
Sie können die Datei auch direkt abschneiden, anstatt sie mit dem<>;
Umleitungsoperator neu zu schreiben :iconv + head
Um die ersten 1234 Zeichen zu drucken , besteht eine andere Möglichkeit darin, in eine Kodierung mit einer festen Anzahl von Bytes pro Zeichen wie
UTF32BE
/ zu konvertierenUCS-4
:head -c
ist nicht Standard, aber ziemlich häufig. Ein Standardäquivalent wäredd bs=1 count="$((1234 * 4))"
jedoch weniger effizient, da es die Eingabe lesen und die Ausgabe byteweise schreiben würde¹.iconv
ist ein Standardbefehl, aber die Codierungsnamen sind nicht standardisiert, sodass Sie möglicherweise Systeme ohne findenUCS-4
Anmerkungen
Obwohl die Ausgabe höchstens 1234 Zeichen hat, ist sie möglicherweise kein gültiger Text, da sie möglicherweise in einer nicht begrenzten Zeile endet.
Beachten Sie auch, dass diese Lösungen zwar keinen Text in der Mitte eines Zeichens ausschneiden, ihn jedoch in der Mitte eines Diagramms
é
unterbrechen könnten , wie beispielsweise U + 0065 U + 0301 (e
gefolgt von einem kombinierenden Akzent). oder Hangul Silbengrapheme in ihren zerlegten Formen.¹ und bei der Pipe-Eingabe können Sie nur dann
bs
zuverlässig Werte außer 1 verwenden, wenn Sie dieiflag=fullblock
GNU-Erweiterung verwenden, dadd
kurze Lesevorgänge möglich sind, wenn die Pipe schneller gelesen wird, als sieiconv
gefüllt wirdquelle
dd bs=1234 count=4
Wenn Sie wissen, dass die Textdatei als UTF-8 codiertes Unicode enthält, müssen Sie zuerst das UTF-8 decodieren, um eine Sequenz von Unicode-Zeicheneinheiten zu erhalten und diese zu teilen.
Ich würde Python 3.x für den Job wählen.
Mit Python 3.x verfügt die Funktion open () über ein zusätzliches Schlüsselwortargument
encoding=
zum Lesen von Textdateien . Die Beschreibung der Methode io.TextIOBase.read () sieht vielversprechend aus.Unter Verwendung von Python 3 würde dies folgendermaßen aussehen:
Offensichtlich würde ein echtes Tool Befehlszeilenargumente, Fehlerbehandlung usw. hinzufügen.
Mit Python 2.x können Sie ein eigenes dateiähnliches Objekt implementieren und die Eingabedatei zeilenweise dekodieren.
quelle
Ich möchte einen anderen Ansatz hinzufügen. Wahrscheinlich nicht die beste Leistung und viel länger, aber einfach zu verstehen:
Rufe es mit auf
$ ./scriptname <desired chars> <input file>
.Dadurch wird das letzte Zeichen nacheinander entfernt, bis das Ziel erreicht ist. Dies scheint besonders bei größeren Dateien eine sehr schlechte Leistung zu sein. Ich wollte dies nur als Idee präsentieren, um mehr Möglichkeiten aufzuzeigen.
quelle
wc
Zählt für eine Datei mit der Länge n in der Größenordnung von 0 (n ^ 2) Gesamtbytes für einen Zielpunkt auf halbem Weg in der Datei. Es sollte möglich sein, eine Binärsuche anstelle einer linearen Suche durchzuführen, indem Sie eine Variable verwenden, die Sie vergrößern oder verkleinernecho -n "${result::-$chop}" | wc -m
. (Und wenn Sie schon dabei sind, machen Sie es sicher, auch wenn der Inhalt der Datei mit-e
oder etwas beginnt , vielleicht mitprintf
). Aber Sie werden Methoden, die jedes Eingabezeichen nur einmal betrachten, immer noch nicht schlagen, es lohnt sich also wahrscheinlich nicht.$result
bis die gewünschte Länge erreicht ist. Wenn die gewünschte Länge jedoch eine hohe Zahl ist, ist sie genauso ineffizient.$desired_chars
Bytes am unteren Ende oder vielleicht4*$desired_chars
am oberen Ende beginnen. Trotzdem denke ich, dass es am besten ist, etwas ganz anderes zu verwenden.