dizgiyi değiştirmek için çizgi olmayan araç?


13

Son zamanlarda, başka bir karakterden sonra ortaya çıkarsa, yeni satır karakterinin nasıl kaldırılacağı hakkında bir soru sordum .

Unix metin işleme araçları çok güçlüdür, ancak neredeyse hepsi metin satırlarıyla ilgilenir, bu da girişin kullanılabilir belleğe sığması çoğu zaman iyidir.

Ancak, herhangi bir satırsonu içermeyen büyük bir dosyadaki bir metin sırasını değiştirmek istersem ne yapmalıyım?

Örneğin yerini <foobar>ile \n<foobar>giriş hattını-by-line okumadan? (çünkü sadece bir satır vardır ve 2.5G karakter uzunluğundadır).


1
perlVeya kullanımına açık mısınız python?
iruvar

Perl iyidir. Sadece deneyeceğimgsar ( home.online.no/~tjaberg ) buldum .
MattBianco

Yanıtlar:


12

Bu tür bir sorunla karşılaştığımda ortaya çıkan ilk şey, kayıt ayırıcıyı değiştirmektir. Çoğu araçta, bu \nvarsayılan olarak ayarlıdır, ancak değiştirilebilir. Örneğin:

  1. Perl

    perl -0x3E -pe 's/<foobar>/\n$&/' file
    

    açıklama

    • -0: bu girdi kayıt ayırıcısını onaltılık değeri verilen bir karaktere ayarlar . Bu durumda, >hex değeri olana ayarlıyorum 3E. Genel biçim -0xHEX_VALUE. Bu sadece çizgiyi yönetilebilir parçalara bölmenin bir hilesi.
    • -pe: tarafından verilen komut dosyasını uyguladıktan sonra her giriş satırını yazdırın -e.
    • s/<foobar>/\n$&/: basit bir ikame. $&Bu durumda, eşleştirildi odur <foobar>.
  2. awk

    awk '{gsub(/foobar>/,"\n<foobar>");printf "%s",$0};' RS="<" file
    

    açıklama

    • RS="<": giriş kayıt ayırıcısını olarak ayarlamanızı sağlar >.
    • gsub(/foobar>/,"\n<foobar>"): Tüm durumlarda yerine foobar>sahip \n<foobar>. Olarak RSayarlandığından <, hepsinin <giriş dosyasından kaldırıldığını (işte bu şekilde awkçalışır), bu yüzden eşleştirmemiz foobar>(a olmadan <) ve ile değiştirmemiz gerektiğini unutmayın \n<foobar>.
    • printf "%s",$0: değişiklikten sonra geçerli "satırı" yazdırır. $0o anki kayıttır bu awkyüzden daha önce ne varsa onu tutacaktır <.

Bunları şu komutlarla oluşturulan 2.3 GB'lık tek satırlı bir dosyada test ettim:

for i in {1..900000}; do printf "blah blah <foobar>blah blah"; done > file
for i in {1..100}; do cat file >> file1; done
mv file1 file

Hem awkve perlkullanılan ihmal edilebilir miktarda bellek.


Hiç Tie::File perldoc.perl.org/Tie/File.html dosyasını denediniz mi ? Bence Perlbüyük dosyalarla uğraşırken en iyi özellikleri .
cuonglm

@Gnouc Bununla biraz oynadım, evet. Ama i) OP zaten başka bir soruda Perl'den hoşlanmadığını iddia etti, bu yüzden basit tutmak istedim ii) Kesinlikle gerekli olmadıkça harici modüller kullanmaktan kaçınma eğilimindeyim ve iii) Tie :: File modülünü kullanmak sözdizimini oldukça az yapar açık.
terdon

Katılıyorum. O Tie::Filezamandan beri çekirdek bir modül olan küçük bir not v5.7.3.
cuonglm

9

gsar (genel arama ve değiştirme) tam olarak bu amaç için çok faydalı bir araçtır.

Bu sorunun cevabının çoğu, soruna uyum sağlamak için kayıt tabanlı araçlar ve çeşitli hileler kullanır; örneğin, varsayılan kayıt ayırıcı karakterini, her bir kaydı işlemek için çok büyük yapmamak için girişte yeterince sık olduğu varsayılan bir şeye değiştirmek gibi.

Çoğu durumda bu çok iyi ve hatta okunabilir. Ben verimli gibi her yerde-mevcut araçlarla çözülmesi kolayca olabilir problemler / sevmiyorum awk, tr, sedve Bourne kabuğu.

Rasgele içeriklerle rastgele büyük bir dosyada ikili bir arama ve değiştirme yapmak bu standart unix araçları için çok uygun değildir.

Bazılarınız bunun hile olduğunu düşünebilir, ancak iş için doğru aracı kullanmanın nasıl yanlış olabileceğini göremiyorum. Bu durumda adında bir C programıdır gsaraltında lisanslanmıştır GPL v2 bana hiçbir hiçbiri bu çok kullanışlı araç için paket olduğunu biraz şaşırtıyor yüzden, gentoo , redhat , ne de ubuntu .

gsarBoyer-Moore string arama algoritmasının ikili bir varyantını kullanır .

Kullanımı basittir:

gsar -F '-s<foobar>' '-r:x0A<foobar>'

burada -F"filtre" modu, yani okuma stdinyazma anlamına gelir stdout. Dosyalar üzerinde de çalışma yöntemleri vardır. -sarama dizesini ve -rdeğiştirmeyi belirtir . İki nokta üst üste işareti rasgele bayt değerlerini belirtmek için kullanılabilir.

Büyük / küçük harf duyarsız mod desteklenir ( -i), ancak algoritma aramayı optimize etmek için arama dizesinin uzunluğunu kullandığından düzenli ifadeler için destek yoktur.

Araç aynı zamanda biraz arama yapmak için de kullanılabilir grep. gsar -bçıkışlar bayt eşleşen arama dizesinin uzaklıklar ve gsar -lbaskılar kütükadı ve eşleşme sayısı, varsa biraz birleştirmek gibi grep -lolan wc.

Araç Tormod Tjaberg (başlangıç) ve Hans Peter Verne (geliştirmeler) tarafından yazılmıştır .



1
Aslında bunun için bir gentoo ebuild'i yapmayı ciddi olarak düşünüyorum. Belki de bir rpm. Ama daha önce hiç bir .deb paketi inşa etmedim, umarım birisi beni yener (çünkü bu biraz zaman alacaktır).
MattBianco

Bunun çok teselli olduğunu sanıyorum ama OS X'in homebrew'unun formülü var gsar.
crazysim

5

Hedef ve değiştirme dizelerinin aynı uzunlukta olduğu dar durumda, bellek eşlemesi kurtarmaya gelebilir. Bu özellikle değiştirme işleminin yerinde yapılması gerektiğinde yararlıdır. Temelde bir dosyayı bir işlemin sanal belleğine eşliyorsunuz ve 64 bit adresleme için adres alanı çok büyük. Dosyanın bir kerede fiziksel belleğe eşlenmesinin gerekmediğini unutmayın , bu nedenle makinede bulunan fiziksel belleğin birkaç katı büyüklüğünde dosyalar ele alınabilir.

İşte bir Python örneği olduğunu cümledeki var foobarolanXXXXXX

#! /usr/bin/python
import mmap
import contextlib   
with open('test.file', 'r+') as f:
 with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)) as m:
   pos = 0
   pos = m.find('foobar', pos)
   while pos > 0:
    m[pos: pos+len('XXXXXX')] = 'XXXXXX'
    pos = m.find('foobar', pos)

4

Bunun için birçok araç var:

ddbir dosyayı engellemek istiyorsanız kullanmak istediğiniz şeydir - yalnızca belirli sayıda baytı yalnızca belirli bir sayıda güvenilir bir şekilde okuyun. Dosya akışlarını engelleme ve engellemeyi kaldırmayı portatif olarak işler:

tr -dc '[:graph:]' </dev/urandom | dd bs=32 count=1 cbs=8 conv=unblock,sync 2>/dev/null

###OUTPUT###

UI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)N

Ben de tryukarıdaki herhangi bir ASCII bayt başka bir dönüştürme (veya bu durumda, boşluk yazdırılamaz bir karakter olmayan herhangi bir ASCII bayt silme) işleyebilir çünkü yukarıda kullanın . Bu sabah diğer sorunuza cevap olarak kullandığım şey , aslında, bunu yaptığımda:

tr '>\n' '\n>' | sed 's/^>*//' | tr '\n>' '>\n' 

Orada birçok benzer . Bu liste, aşina olabileceğiniz en düşük ortak payda alt kümesini sağlamalıdır.

Ancak, 2.5 gbs ikili dosya üzerinde metin işleme yapacak olsaydım, başlayabilirim od. Size başka bir octal dumpveya daha fazla format verebilir . Her türlü seçeneği belirtebilirsiniz - ancak her satırda yalnızca bir bayt \Ckaçış biçiminde yapacağım :

odAşağıdan gösterdiğim gibi , alacağınız veriler belirttiğiniz aralıkta düzenli olacaktır. Ama önce - sorunuzun cevabı burada:

printf 'first\nnewline\ttab spacefoobar\0null' |
od -A n -t c -v -w1 |
sed 's/^ \{1,3\}//;s/\\$/&&/;/ /bd
     /\\[0nt]/!{H;$!d};{:d
    x;s/\n//g}'

Bu biraz yukarıda \newlines, \0nulls, \tabs ve sınırlayıcı için kaçan dize <spaces>korunurken sınırlar \C. Kullanılan Hve xişlevlerine dikkat edin; her sedbir sınırlayıcıyla karşılaştığında, bellek arabelleklerinin içeriğini değiştirir. Bu şekilde, sedyalnızca dosyayı güvenilir bir şekilde sınırlamak için gerektiği kadar bilgi tutar ve arabellek taşmalarına boyun eğmez - yani aslında sınırlayıcılarıyla karşılaştığı sürece. Kadar uzun olduğu gibi için, sedonun girişini işlenmeye devam edecek ve odkarşılaştığı kadar vermeye devam edecektir EOF.

Olduğu gibi, çıktısı şöyle görünür:

first
\nnewline
\ttab
 spacefoobar
\0null

Yani eğer istersem foobar:

printf ... | od ... | sed ... | 
sed 's/foobar/\
&\
/g'

###OUTPUT###

first
\nnewline
\ttab
 space
foobar

\0null

Eğer Ckaçışları kullanmak istiyorsanız, bu oldukça kolaydır - çünkü sedzaten çift \\ters eğik çizgi tüm tek giriş ters eğik çizgilerden kaçmıştır, bu nedenle printfyürütülen xargsözellik belirtiminize çıktı üreten herhangi bir sorun olmayacaktır. Ancak xargs kabuk tırnaklarını yiyor, böylece tekrar iki kez alıntı yapmanız gerekecek:

printf 'nl\ntab\tspace foobarfoobar\0null' |
PIPELINE |
sed 's/./\\&/g' | 
xargs printf %b | 
cat -A

###OUTPUT###

nl$
tab^Ispace $
foobar$
$
foobar$
^@null%

Bu, bir kabuk değişkenine kolayca kaydedilebilir ve daha sonra aynı şekilde üretilebilir. Sonuncusu , girdisindeki her karakterden önce ters eğik çizgi sedekler \ve hepsi bu.

Ve işte daha önce hiç olmadığı gibi görünüyor sed:

printf 'nl\ntab\tspace foobarfoobar\0null' |
od -A n -t c -v -w1

   n
   l
  \n
   t
   a
   b
  \t
   s
   p
   a
   c
   e

   f
   o
   o
   b
   a
   r
   f
   o
   o
   b
   a
   r
  \0
   n
   u
   l
   l

2

Awk ardışık kayıtlar üzerinde çalışır. Kayıt ayırıcı olarak herhangi bir karakteri kullanabilir (birçok uygulamada null bayt hariç). Bazı uygulamalar, kayıt ayırıcısı olarak rasgele düzenli ifadeleri (boş dizeyle eşleşmeyen) destekler, ancak bu gereksiz olabilir, çünkü kayıt ayırıcısı içine yerleştirilmeden önce her kaydın sonundan kesilir $0(GNU awk değişkeni RTkayıt ayırıcısına ayarlar mevcut kaydın sonundan çıkarılmıştır). Varsayılan olarak yeni satır olan ve giriş kayıt ayırıcısından bağımsız olarak ayarlanan printçıkış kayıt ayırıcısı ile çıkışını sonlandırdığını unutmayın .ORSRS

awk -v RS=, 'NR==1 {printf "input up to the first comma: %s\n", $0}'

Etkili (diğer araçlar için rekor ayırıcı olarak farklı bir karakter seçebilir sort, sedo karakterle yeni satır değiştirerek, ...) tr.

tr '\n,' ',\n' |
sed 's/foo/bar/' |
sort |
tr '\n,' ',\n'

Birçok GNU metin yardımcı programı, ayırıcı olarak yeni satır yerine boş bir bayt kullanılmasını destekler.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.