วิธีตัดไฟล์ให้มีจำนวนอักขระสูงสุด (ไม่ใช่ไบต์)


13

ฉันจะตัดทอนไฟล์ข้อความ (UTF-8 ที่เข้ารหัส) เป็นจำนวนอักขระได้อย่างไร ฉันไม่สนใจความยาวของเส้นและการตัดอาจอยู่ตรงกลางคำ

  • cut ดูเหมือนว่าจะทำงานบนบรรทัด แต่ฉันต้องการไฟล์ทั้งหมด
  • head -c ใช้ไบต์ไม่ใช่ตัวอักษร

โปรดทราบว่าการนำ GNU ไปใช้cutยังคงไม่รองรับอักขระหลายไบต์ cut -zc-1234 | tr -d '\0'ถ้ามันไม่ได้คุณสามารถทำ
Stéphane Chazelas

คุณต้องการจัดการอิโมจิอย่างไร บางตัวละครมากกว่านั้น ... stackoverflow.com/questions/51502486/
phuzi

2
ตัวละครคืออะไร? สัญลักษณ์บางตัวใช้คะแนนรหัสหลายจุด
Jasen

คำตอบ:


14

บางระบบมีtruncateคำสั่งที่ตัดทอนไฟล์เป็นจำนวนไบต์ (ไม่ใช่ตัวอักษร)

ฉันไม่รู้ว่ามีการตัดทอนอักขระจำนวนมากถึงแม้ว่าคุณสามารถใช้วิธีการperlติดตั้งตามค่าเริ่มต้นในระบบส่วนใหญ่:

Perl

perl -Mopen=locale -ne '
  BEGIN{$/ = \1234} truncate STDIN, tell STDIN; last' <> "$file"
  • ด้วย-Mopen=localeเราใช้แนวคิดของโลแคลว่าอักขระคืออะไร (ดังนั้นในโลแคลที่ใช้ชุดอักขระ UTF-8 นั่นคืออักขระที่เข้ารหัส UTF-8) แทนที่ด้วย-CSหากคุณต้องการให้ I / O ถอดรหัส / เข้ารหัสเป็น UTF-8 โดยไม่คำนึงถึงชุดอักขระของโลแคล

  • $/ = \1234: เราตั้งค่าตัวคั่นเร็กคอร์ดเป็นการอ้างอิงถึงจำนวนเต็มซึ่งเป็นวิธีการระบุเร็กคอร์ดของความยาวคงที่ (ในจำนวนอักขระ )

  • จากนั้นเมื่ออ่านเร็กคอร์ดแรกเราตัด stdin ให้เข้าที่ (ดังนั้นในตอนท้ายของเรคคอร์ดแรก) และออก

GNU sed

ด้วย GNU sedคุณสามารถทำได้ (สมมติว่าไฟล์ไม่มีอักขระ NUL หรือลำดับของไบต์ที่ไม่ได้ใช้อักขระที่ถูกต้องซึ่งทั้งสองอย่างนี้ควรเป็นจริงสำหรับไฟล์ข้อความ):

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

แต่มันมีประสิทธิภาพน้อยกว่ามากเนื่องจากจะอ่านไฟล์แบบเต็มและเก็บไว้ในหน่วยความจำทั้งหมดและเขียนสำเนาใหม่

GNU awk

เหมือนกับ 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='^$': โหมด Slurp

เชลล์ในตัว

ด้วย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หรือbash(ระวังเป็นของปลอมสำหรับอักขระหลายไบต์ในหลาย ๆ เวอร์ชันbash ):

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

ksh93ยังสามารถตัดทอนไฟล์แทนการเขียนใหม่ด้วย<>;ตัวดำเนินการเปลี่ยนเส้นทาง:

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

iconv + head

หากต้องการพิมพ์อักขระ 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 ตัวอักษร แต่ท้ายที่สุดอาจไม่ใช่ข้อความที่ถูกต้องเนื่องจากอาจสิ้นสุดในบรรทัดที่ไม่มีการคั่น

โปรดทราบว่าในขณะที่คำตอบเหล่านั้นจะไม่ตัดข้อความที่อยู่ตรงกลางของตัวละคร แต่พวกเขาสามารถแยกมันออกมากลางกราฟเช่นการéแสดงเป็น U + 0065 U + 0301 ( eตามด้วยสำเนียงเฉียบพลันที่รวมกัน) หรืออังกูลพยางค์กราฟในรูปแบบที่ย่อยสลาย


¹และในการป้อนข้อมูลไปป์คุณไม่สามารถใช้bsค่าอื่นที่ไม่ใช่ 1 ได้อย่างน่าเชื่อถือเว้นแต่ว่าคุณจะใช้iflag=fullblockส่วนขยาย GNU เช่นเดียวกับที่ddสามารถอ่านค่าสั้น ๆ หากมันอ่านไปป์เร็วกว่าiconvเติม


ทำได้dd bs=1234 count=4
Jasen

2
@ Jasen นั้นจะไม่น่าเชื่อถือ ดูการแก้ไข
Stéphane Chazelas

ว้าว! คุณจะมีประโยชน์ที่จะมีสถานที่ใกล้เคียง! ฉันคิดว่าฉันรู้คำสั่ง Unix ที่มีประโยชน์มากมาย แต่นี่เป็นรายการตัวเลือกที่ยอดเยี่ยม
Mark Stewart

5

หากคุณรู้ว่าไฟล์ข้อความมี Unicode เข้ารหัสเป็น UTF-8 คุณต้องถอดรหัส UTF-8 ก่อนเพื่อให้ได้ลำดับของเอนทิตีอักขระ Unicode และแยกออก

ฉันเลือก Python 3.x สำหรับงาน

กับงูหลาม 3.x ฟังก์ชั่นเปิด ()มีข้อโต้แย้งคำสำคัญเป็นพิเศษencoding=สำหรับการอ่านข้อความไฟล์ คำอธิบายของวิธีการio.TextIOBase.read ()ดูมีแนวโน้ม

ดังนั้นการใช้ Python 3 มันจะเป็นดังนี้:

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

เห็นได้ชัดว่าเครื่องมือจริงจะเพิ่มอาร์กิวเมนต์บรรทัดคำสั่งการจัดการข้อผิดพลาด ฯลฯ

ด้วย Python 2.x คุณสามารถใช้วัตถุที่มีลักษณะคล้ายไฟล์ของคุณเองและถอดรหัสไฟล์อินพุตแบบทีละบรรทัด


ใช่ฉันสามารถทำได้ แต่สำหรับเครื่องสร้าง CI ดังนั้นฉันอยากจะใช้คำสั่ง Linux มาตรฐานอีกครั้ง
Pitel

5
ไม่ว่า "Linux มาตรฐาน" หมายถึงรสชาติของ Linux ...
Michael Ströder

1
แท้จริงแล้ว Python บางรุ่นมันค่อนข้างเป็นมาตรฐานในทุกวันนี้
muru

ฉันได้แก้ไขคำตอบของฉันกับตัวอย่างสำหรับ Python 3 ซึ่งสามารถประมวลผลไฟล์ข้อความได้อย่างชัดเจน
Michael Ströder

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จนถึงจนกว่าจะตรงกับความยาวที่ต้องการ แต่ถ้าความยาวที่ต้องการเป็นจำนวนสูงมันก็ไม่มีประสิทธิภาพ
ลูกปา

1
คุณสามารถเริ่มต้นใกล้กับสถานที่ที่เหมาะสมโดยเริ่มต้นด้วย$desired_charsไบต์ที่ต่ำสุดหรืออาจจะเป็น4*$desired_charsที่สูง แต่ถึงกระนั้นฉันคิดว่ามันเป็นการดีที่สุดที่จะใช้อย่างอื่นอย่างสมบูรณ์
ปีเตอร์กอร์เดส
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.