फ़ाइल को अधिकतम वर्णों तक कैसे काटें (बाइट्स नहीं)


13

मैं वर्णों की संख्या के लिए एक (UTF-8 एन्कोडेड) पाठ फ़ाइल को कैसे काट सकता हूं? मुझे लाइन की लंबाई की परवाह नहीं है और कट शब्द के बीच में हो सकता है।

  • cut लाइनों पर काम करने लगता है, लेकिन मुझे पूरी फाइल चाहिए।
  • head -c बाइट्स का उपयोग करता है, वर्णों का नहीं।

ध्यान दें कि GNU कार्यान्वयन cutअभी भी मल्टी-बाइट वर्णों का समर्थन नहीं करता है। अगर ऐसा होता, तो आप कर सकते थे cut -zc-1234 | tr -d '\0'
स्टीफन चेजलस

आप इमोजीस को कैसे संभालना चाहते हैं? कुछ और हैं कि एक चरित्र ... stackoverflow.com/questions/51502486/…
फ़ूजी

2
एक चरित्र क्या है? कुछ प्रतीकों में कई कोड बिंदुओं का उपयोग किया गया है,
जैसन

जवाबों:


14

कुछ सिस्टम में एक truncateकमांड होती है जो फाइलों को कई बाइट्स में ले जाती है (अक्षर नहीं)।

मुझे किसी भी वर्ण का पता नहीं है जो कई वर्णों को काटता है, हालाँकि आप इसका सहारा ले सकते हैं perlजो कि अधिकांश प्रणालियों पर डिफ़ॉल्ट रूप से स्थापित है:

पर्ल

perl -Mopen=locale -ne '
  BEGIN{$/ = \1234} truncate STDIN, tell STDIN; last' <> "$file"
  • इसके साथ -Mopen=locale, हम लोकेल की धारणा का उपयोग करते हैं कि कौन से वर्ण हैं (इसलिए UTF-8 वर्णमाला का उपयोग करने वाले स्थानों में, वह UTF-8 एन्कोडेड वर्ण हैं)। -CSयदि आप चाहते हैं कि मैं / O को यूटीएफ -8 में डीकोड / एनकोडेड किया जाए, तो लोकेल के चारसेट की परवाह किए बिना बदलें ।

  • $/ = \1234: हम रिकॉर्ड विभाजक को एक पूर्णांक के संदर्भ में सेट करते हैं जो निश्चित लंबाई ( वर्णों की संख्या ) में रिकॉर्ड निर्दिष्ट करने का एक तरीका है ।

  • तब पहला रिकॉर्ड पढ़ने के बाद, हम जगह में स्टड को अलग कर देते हैं (इसलिए पहले रिकॉर्ड के अंत में) और बाहर निकलें।

GNU sed

GNU के साथ sed, आप ऐसा कर सकते हैं (यह मानते हुए कि फ़ाइल में NUL वर्ण या बाइट्स के अनुक्रम नहीं हैं जो वैध वर्ण नहीं बनाते हैं - दोनों को पाठ फ़ाइलों का सच होना चाहिए):

sed -Ez -i -- 's/^(.{1234}).*/\1/' "$file"

लेकिन यह बहुत कम कुशल है, क्योंकि यह फ़ाइल को पूर्ण रूप से पढ़ता है और इसे पूरी मेमोरी में संग्रहीत करता है, और एक नई प्रतिलिपि लिखता है।

जीएनयू जागा

GNU के साथ भी awk:

awk -i inplace -v RS='^$' -e '{printf "%s", substr($0, 1, 1234)}' -E /dev/null "$file"
  • -e code -E /dev/null "$file" एक तरह से मनमानी फ़ाइल नामों को पारित करने के लिए किया जा रहा है gawk
  • RS='^$': स्लरप मोड

शेल का निर्माण किया

के साथ ksh93, bashया zsh(गोले के अलावा zsh, यह मानते हुए कि सामग्री में NUL बाइट्स नहीं हैं):

content=$(cat < "$file" && echo .) &&
  content=${content%.} &&
  printf %s "${content:0:1234}" > "$file"

के साथ zsh:

read -k1234 -u0 s < $file &&
  printf %s $s > $file

या:

zmodload zsh/mapfile
mapfile[$file]=${mapfile[$file][1,1234]}

( ksh93या इसके कई संस्करणों में बहु-बाइट वर्णों के लिए फर्जी है ) के साथ:bashbash

IFS= read -rN1234 s < "$file" &&
  printf %s "$s" > "$file"

ksh93फ़ाइल को उसके <>;पुनर्निर्देशन ऑपरेटर के साथ पुन: लिखने के स्थान पर काट-छाँट भी कर सकता है :

IFS= read -rN1234 0<>; "$file"

आइकनव + सिर

पहले 1234 वर्णों को मुद्रित करने के लिए , एक अन्य विकल्प को एन्कोडिंग में परिवर्तित किया जा सकता है, जैसे प्रति वर्ण बाइट्स की निश्चित संख्या के साथ UTF32BE/ UCS-4:

iconv -t UCS-4 < "$file" | head -c "$((1234 * 4))" | iconv -f UCS-4

head -cमानक नहीं है, लेकिन काफी सामान्य है। एक मानक समतुल्य होगा, dd bs=1 count="$((1234 * 4))"लेकिन कम कुशल होगा, क्योंकि यह इनपुट को पढ़ेगा और आउटपुट को एक बार में एक बाइट लिख देगा। iconvएक मानक कमांड है, लेकिन एन्कोडिंग नाम मानकीकृत नहीं हैं, इसलिए आप सिस्टम को बिना खोज सकते हैंUCS-4

टिप्पणियाँ

किसी भी मामले में, हालांकि आउटपुट में अधिकतम 1234 अक्षर होंगे, यह अंत में मान्य पाठ नहीं हो सकता है, क्योंकि यह संभवतः एक गैर-सीमांकित रेखा में समाप्त होगा।

यह भी ध्यान दें कि जब वे समाधान किसी पात्र के बीच में पाठ को नहीं काटेंगे, तो वे इसे ग्रैफेम के बीच में तोड़ सकते हैं , जैसे éकि यू + 0065 यू + 0301 (एक eतीव्र उच्चारण के संयोजन के बाद) के रूप में व्यक्त किया जाता है, या हंगुल शब्दांश अंगूर अपने विघटित रूपों में।


¹ और पाइप इनपुट पर आप उपयोग नहीं कर सकते bs1 मज़बूती के अतिरिक्त अन्य मान जब तक आप का उपयोग iflag=fullblock, जीएनयू विस्तार के रूप में ddकम पढ़ता है अगर यह पाइप तेज पढ़ता से कर सकता है iconvभरण यह


कर सकता हैdd bs=1234 count=4
जैसन

2
@ जासेन, यह विश्वसनीय नहीं होगा। संपादित देखें।
स्टीफन चेजलस

वाह! आप पास होना आसान होगा! मुझे लगा कि मैं बहुत सारे यूनिक्स कमांड जानता हूं, लेकिन यह शानदार विकल्पों की एक अविश्वसनीय सूची है।
मार्क स्टीवर्ट

5

यदि आप जानते हैं कि पाठ फ़ाइल में यूनिकोड कूटबद्ध है तो यूटीएफ -8 के रूप में आपको यूनिकोड वर्ण संस्थाओं का अनुक्रम प्राप्त करने के लिए यूटीएफ -8 को पहले डीकोड करना होगा और उन को विभाजित करना होगा।

मैं नौकरी के लिए अजगर 3.x चुनूंगा।

पायथन 3.x के साथ फंक्शन ओपन () में टेक्स्ट-फाइलencoding= पढ़ने के लिए एक अतिरिक्त की-वर्ड तर्क है । विधि का विवरण io.TextIOBase.read () आशाजनक लगता है।

तो पायथन 3 का उपयोग करना इस तरह दिखेगा:

truncated = open('/path/to/file.txt', 'rt', encoding='utf-8').read(1000)

जाहिर है एक वास्तविक उपकरण कमांड-लाइन तर्क, त्रुटि हैंडलिंग आदि को जोड़ देगा।

पायथन 2.x के साथ आप अपनी खुद की फ़ाइल जैसी वस्तु को कार्यान्वित कर सकते हैं और इनपुट फ़ाइल लाइन-बाय-लाइन को डीकोड कर सकते हैं।


हाँ, मैं ऐसा कर सकता था। लेकिन यह CI बिल्ड मशीनों के लिए है, इसलिए मैं कुछ मानक लिनक्स कमांड का उपयोग करना चाहता हूं।
पिटेल

5
आपके लिनक्स स्वाद पर जो भी "मानक लिनक्स" का अर्थ है ...
माइकल स्ट्रोडर

1
वास्तव में, पायथन, इसके कुछ संस्करण वैसे भी, इन दिनों सुंदर मानक है।
मूरू

मैंने पहले ही पायथन 3 के लिए स्निपेट के साथ अपना उत्तर संपादित कर दिया है, जो स्पष्ट रूप से पाठ-फाइलों को संसाधित कर सकता है।
माइकल स्ट्रोडर

0

मैं एक और दृष्टिकोण जोड़ना चाहूंगा। शायद सबसे अच्छा प्रदर्शन बुद्धिमान नहीं है, और बहुत लंबा है, लेकिन समझने में आसान है:

#!/bin/bash

chars="$1"
ifile="$2"
result=$(cat "$ifile")
rcount=$(echo -n "$result" | wc -m)

while [ $rcount -ne $chars ]; do
        result=${result::-1}
        rcount=$(echo -n "$result" | wc -m)
done

echo "$result"

इसके साथ आह्वान करें $ ./scriptname <desired chars> <input file>

यह लक्ष्य पूरा होने तक एक-एक करके पिछले चार को हटा देता है, जो विशेष रूप से बड़ी फ़ाइलों के लिए वास्तव में बुरा प्रदर्शन बुद्धिमान लगता है। मैं इसे अधिक संभावनाओं को दिखाने के लिए एक विचार के रूप में प्रस्तुत करना चाहता था।


हाँ, यह निश्चित रूप से प्रदर्शन के लिए भयानक है। लंबाई n की फ़ाइल के लिए, फ़ाइल में wcलक्ष्य बिंदु आधे रास्ते के लिए O (n ^ 2) कुल बाइट्स के आदेश पर गिना जाता है। एक चर का उपयोग करके रैखिक खोज के बजाय द्विआधारी-खोज करना संभव है जिसे आप बढ़ाते हैं या घटाते हैं, जैसे echo -n "${result::-$chop}" | wc -mया कुछ और। (और जब आप इस पर हों, तब भी इसे सुरक्षित रखें, भले ही फ़ाइल सामग्री -eया कुछ के साथ शुरू हो, शायद उपयोग कर रही हो printf)। लेकिन आप अभी भी उन तरीकों को नहीं हराएंगे जो केवल प्रत्येक इनपुट चरित्र को एक बार देखते हैं, इसलिए शायद इसके लायक नहीं है।
पीटर कॉर्ड्स

आप निश्चित रूप से सही हैं, एक व्यावहारिक जवाब के बजाय एक तकनीकी उत्तर के अधिक। $resultजब तक यह वांछित लंबाई से मेल नहीं खाता, तब तक आप इसे char द्वारा char जोड़ने के लिए उल्टा कर सकते थे , लेकिन यदि वांछित लंबाई एक उच्च संख्या है तो यह अक्षम के रूप में है।
कंफ़ेद्दी

1
आप $desired_charsकम अंत में बाइट्स के साथ शुरू करके , या शायद 4*$desired_charsउच्च अंत में सही जगह के करीब शुरू कर सकते हैं । लेकिन फिर भी मुझे लगता है कि पूरी तरह से कुछ और उपयोग करना सबसे अच्छा है।
पीटर कॉर्ड्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.