bash - يدعم - كيفية التعديل على ملف pdf(حذف واضافة نص)




فرز ملف نصي حسب طول الخط بما في ذلك المسافات (6)

إجابة

cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-

أو للقيام بالفرز الفرعي الأصلي (ربما غير المقصود) لأي خطوط متساوية الطول:

cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-

في كلتا الحالتين ، قمنا بحل مشكلتك المعلنة من خلال الابتعاد عن awk لخفضك النهائي.

خطوط طول المطابقة - ما يجب فعله في حالة التعادل:

لم يحدد السؤال ما إذا كان هناك حاجة إلى مزيد من الفرز لخطوط طول المطابقة أم لا. لقد افترضت أن هذا غير مرغوب فيه واقترح استخدام -s ( --stable ) لمنع هذه الخطوط يتم فرزها ضد بعضها البعض ، والاحتفاظ بها في الترتيب النسبي الذي تحدث فيه في الإدخال.

(قد يرغب أولئك الذين يريدون مزيدًا من التحكم في فرز هذه الروابط في خيار - --key الاختيار).

لماذا فشل حل محاولة السؤال (awk line-rebuilding):

من المثير للاهتمام ملاحظة الفرق بين:

echo "hello   awk   world" | awk '{print}'
echo "hello   awk   world" | awk '{$1="hello"; print}'

ينتجون على التوالي

hello   awk   world
hello awk world

يذكر الجزء ذو الصلة من دليل (gawk) فقط أنه جانبا أن awk سيعيد بناء كامل $ 0 (على أساس الفاصل ، الخ) عندما تقوم بتغيير حقل واحد. أعتقد أنه ليس سلوكًا مجنونًا لديها هذا:

"أخيرًا ، هناك أوقات يكون فيها من المناسب فرض قوة إعادة بناء السجل بأكمله ، باستخدام القيمة الحالية للحقول و OFS. للقيام بذلك ، استخدم المهمة التي تبدو غير ضارة:"

 $1 = $1   # force record to be reconstituted
 print $0  # or whatever else with $0

"هذه القوى تثور لإعادة بناء السجل".

اختبار الإدخال بما في ذلك بعض الخطوط ذات الطول المتساوي:

aa A line   with     MORE    spaces
bb The very longest line in the file
ccb
9   dd equal len.  Orig pos = 1
500 dd equal len.  Orig pos = 2
ccz
cca
ee A line with  some       spaces
1   dd equal len.  Orig pos = 3
ff
5   dd equal len.  Orig pos = 4
g

لدي ملف CSV يشبه هذا

AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Atlantis,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mrs. Plain Example, 1121110 Ternary st.                                        110 Binary ave..,Atlantis,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mr. Plain Example, 110 Binary ave.,Liberty City,RI,12345,(999)123-5555,1.56
AS2345,ASDF1232, Mr. Plain Example, 110 Ternary ave.,Some City,RI,12345,(999)123-5555,1.56

أحتاج لفرزها حسب طول الخط بما في ذلك المسافات. لا يتضمن الأمر التالي مسافات ، هل هناك طريقة لتعديلها بحيث تعمل بالنسبة لي؟

cat [email protected] | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'

Pure Bash:

declare -a sorted

while read line; do
  if [ -z "${sorted[${#line}]}" ] ; then          # does line length already exist?
    sorted[${#line}]="$line"                      # element for new length
  else
    sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length
  fi
done < data.csv

for key in ${!sorted[*]}; do                      # iterate over existing indices
  echo -e "${sorted[$key]}"                       # echo lines with equal length
done

جرب هذا الأمر بدلا من ذلك:

awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-

لقد وجدت أن هذه الحلول لن تعمل إذا احتوى الملف على خطوط تبدأ برقم ، نظرًا لأنه سيتم فرزها رقميًا مع جميع الخطوط المعدودة. الحل هو إعطاء sort العلامة -g (عام - رقمي - فرز) بدلاً من -n (فرز رقمي):

awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-

وهنا طريقة متوافقة مع multibyte لفرز الخطوط حسب الطول. يتطلب:

  1. wc -m متاح لك (macOS لديه).
  2. تعتمد اللغة الحالية الخاصة بك على أحرف متعددة البايتات ، على سبيل المثال ، عن طريق تعيين LC_ALL=UTF-8 . يمكنك تعيين هذا إما في ملف .bash_profile الخاص بك ، أو ببساطة عن طريق إرفاقه مسبقًا قبل الأمر التالي.
  3. يحتوي testfile على ترميز أحرف يطابق لغتك (على سبيل المثال ، UTF-8).

وهنا الأمر الكامل:

cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-

شرح جزء تلو الآخر:

  • l=$0; gsub(/\047/, "\047\"\047\"\047", l); ← يجعل من نسخة من كل سطر في awk متغير l و double-escapes كل ' بحيث يمكن أن يكون أصداء الخط بأمان كأمر shell ( \047 هو اقتباس مفرد في التدوين الثماني).
  • cmd=sprintf("echo \047%s\047 | wc -m", l); ← هذا هو الأمر الذي سننفذه ، والذي يردد الخط wc -m في wc -m .
  • cmd | getline c; ← تنفيذ الأمر ونسخ قيمة عدد الأحرف التي يتم إرجاعها إلى awk متغير c .
  • close(cmd); ← أﻏﻠﻖ اﻷﻧﺒﻮب إﻟﻰ أﻣﺮ اﻟﻐﻼف ﻟﺘﺠﻨﺐ ﺿﺮب ﺣﺪ ﻟﻨﻈﺎم ﻋﻠﻰ ﻋﺪد اﻟﻤﻠﻔﺎت اﻟﻤﻔﺘﻮﺣﺔ ﻓﻲ ﻋﻤﻠﻴﺔ واﺣﺪة.
  • sub(/ */, "", c); ← يقلص المساحة البيضاء من قيمة عدد الحروف التي يرجعها wc .
  • { print c, $0 } ← يطبع قيمة عدد الأحرف في السطر ، والمسافة ، والسطر الأصلي.
  • | sort -ns | sort -ns ← فرز الخطوط (بواسطة قيم عدد الأحرف prepended) عدديًا ( -n ) ، مع الحفاظ على ترتيب فرز ثابت ( -s ).
  • | cut -d" " -f2- | cut -d" " -f2- ← يزيل قيم عدد الأحرف prepended.

إنها بطيئة (فقط 160 سطر في الثانية على Macbook Pro سريع) لأنها يجب أن تقوم بتنفيذ أمر فرعي لكل سطر.

بدلا من ذلك ، فقط افعل هذا فقط مع gawk (كما في الإصدار 3.1.5 ، gawk هو multibyte على علم) ، والتي ستكون أسرع بشكل ملحوظ. هناك الكثير من المتاعب في القيام بكل عمليات الهروب والانتقال المزدوج لتمرير الخطوط بأمان من خلال أمر shell من awk ، ولكن هذه هي الطريقة الوحيدة التي يمكنني العثور عليها والتي لا تتطلب تثبيت برامج إضافية (لا يتوفر gawk بشكل افتراضي على ماك).


نتائج المعيار

أيضا ، حل بيرل آخر:
perl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file

تم تنفيذ التجارب باستخدام:

  • 10 تدفقات متتابعة على جهاز سريع ، متوسط
  • بيرل 5.24
  • awk 3.1.5 (gawk 4.1.0 مرة ~ 2٪ أسرع)
  • ملف الإدخال هو 550 ميغابايت و 6 ملايين خط فظيع (British National Corpus txt)

النتائج:

  • يأخذ حل fgm bash 400x أطول من حلول awk (باستخدام حالة اختبار مبتورة من 100000 خط). يعمل بشكل جيد ، فقط يستغرق وقتا طويلا.
  • حل jonathan awk استغرق 25 ثانية
  • حل anubhava awk استغرق 24 ثانية
  • حل neillb awk # 2 استغرق 23 ثانية
  • حل neillb awk # 1 استغرق 20 ثانية
  • استغرق حل بلدي بيرل 11.6 ثانية
  • استغرق كاليب بيرل soution 11.2 ثانية




cygwin