خطوط یک فایل متنی را مرتب کنید، تکراریها را حذف کنید و پاکیزهاش کنید
خطوط یک فایل متنی را در مرورگر خودتان مرتب میکند، تکراریها را حذف میکند، فاصلههای اضافی را میزداید و ترتیب را معکوس میکند. مراحل به ترتیب ثابتی اجرا میشوند — حذف فاصلهها، حذف خطوط خالی، حذف تکراریها، مرتبسازی، معکوس کردن — و فایل هرگز به جایی فرستاده نمیشود.
هر فایل متنی UTF-۸ — یک فهرست، یک لاگ، یک خروجی. در همین تب خوانده میشود و هرگز به جایی فرستاده نمیشود.
چگونگی کار
این ابزار چه میکند
یک فایل متنی — فهرستی از آدرسهای ایمیل، فهرست واژهها، یک لاگ، ستونی که از صفحهگسترده جایگذاری شده — را میگیرد و خطوطش را مرتب میکند، تکراریها را حذف میکند، فاصلههای اضافی را میزداید و خطوط خالی را کنار میگذارد. این چهار کار با هم میآیند چون تقریباً هرگز جدا از هم خواسته نمیشوند: فهرستی که به حذف تکراریها نیاز دارد تقریباً همیشه اول به حذف فاصلهها نیاز دارد، وگرنه تکراریها با هم تطبیق نمیکنند.
دلیل انجام این کار اینجا بهجای ویرایشگر متن این است که فایل از دستگاه خارج نمیشود. فهرستی از ایمیلهای مشتریان که در یک کادر آنلاین «حذف خطوط تکراری» جایگذاری شود، به سرور کس دیگری فرستاده شده و این در بیشتر رژیمهای حفاظت از داده یک رویداد نیازمند افشاست. این ابزار فایل را در همان تبی که جلوی شماست میخواند.
روش
مراحل به ترتیب ثابتی اجرا میشوند و همین ترتیب کل مشخصات است:
حذف فاصلهها، سپس حذف خطوط خالی، سپس حذف تکراریها، سپس مرتبسازی، سپس معکوس کردن.
حذف فاصلهها باید پیش از حذف تکراریها بیاید. milk و milk برای کسی که آنها را تایپ کرده یک ورودیاند و برای رایانه دو رشتهٔ متفاوت، پس تکراریزدایی که اول اجرا شود هر دو را باقی میگذارد و خراب به نظر میرسد. حذف خطوط خالی پیش از حذف تکراریها همان استدلال را دارد: وگرنه همهٔ خطوط خالی فایل به یک خط خالی در بالای خروجی فرومیریزند، که برای هیچکس معنای «حذف خطوط خالی» نیست.
معکوس کردن عمداً در آخر میآید تا هر ترتیبی را که مرحلهٔ قبل ساخته برگرداند. این باعث میشود Z تا A بهعلاوهٔ معکوس همان A تا Z باشد، که شبیه یک کار زائد به نظر میرسد و در واقع بررسی این است که این دو مرحله بیسروصدا در هم ادغام نشدهاند.
مرتبسازی با Intl.Collator انجام میشود که همان الگوریتم همچینی یونیکد است که مرورگر از قبل دارد، نه با عملگر <. مقایسهٔ رشتهها با < واحدهای کد UTF-۱۶ را مقایسه میکند و هر بار دو شکست آشکار میسازد: هر حرف بزرگ پیش از هر حرف کوچک مینشیند، پس Zebra پیش از apple میافتد؛ و هر حرف آکساندار پس از z مینشیند، پس Ångström پس از Zulu میافتد، نه کنار Anchor. همچین بهجای پیروی از زبان صفحه روی قواعد انگلیسی ثابت شده، چون یک فایل با یک تنظیمات باید یک فایل تولید کند — در همچینی سوئدی Å حرفی جداگانه است که پس از Z میآید، و دانلودی که با زبان رابط عوض شود باگی است که گزارش کردنش بسیار دشوار است.
مرتبسازی بهجای یک فراخوانی، در بلوکهای بیستهزار خطی انجام میشود که سپس دوبهدو ادغام میشوند. یک مرتبسازی واحد روی یک میلیون خط یک بلوک کار وقفهناپذیر است: بدون نوار پیشرفت، و با دکمهٔ لغوی که تا پایان کاری نمیکند. ادغام بلوکها بین هر بلوک نوبتی به صفحه میدهد. ادغام هرگاه دو خط برابر مقایسه شوند خط سمت چپ را برمیدارد، و همین است که نتیجه را با یک مرتبسازی پایدار واحد یکسان نگه میدارد — و این یکسانی در فایل آزمون در برابر پنجاه هزار خط شبهتصادفی تأیید میشود، نه اینکه فرض شود.
پیش از ادامهٔ مطلب
چهار خط — Anchor، apple، Ångström و Zebra — از A تا Z مرتب میشوند. Ångström کجا مینشیند؟
دوم، میان Anchor و apple — چون ترتیب اینجا Intl.Collator است، همان الگوریتم همچینی یونیکد که مرورگر از قبل دارد، نه عملگر <. مقایسه با < روی همین فهرست هر بار دو شکست آشکار میسازد: حروف بزرگ را پیش از حروف کوچک مینشاند، پس Zebra پیش از apple میافتد؛ و حروف آکساندار را پس از z مینشاند، پس Ångström آخر میافتد. همچین بهجای زبان صفحه روی قواعد انگلیسی ثابت شده، چون یک فایل با یک تنظیمات باید یک فایل تولید کند — در همچینی سوئدی Å واقعاً حرفی جداگانه پس از Z است، و دانلودی که با زبان رابط عوض شود باگی است که هیچکس نمیتواند گزارشش کند.
یک نمونهٔ کارشده
یک فهرست خرید، ذخیرهشده با نام shopping-list.txt، با پیشفرضهای دستنخورده — حذف فاصلهها روشن، حذف خطوط خالی روشن، حذف تکراریها روشن، حساس به بزرگی حروف خاموش، A تا Z:
Bread
milk
Milk
bread
Eggs
milk
هفت خط وارد میشود. حذف فاصلهها Bread را به Bread تبدیل میکند. خط خالی میرود، که یک خط خالی حذفشده است. Milk، bread و milk دوم با نادیده گرفتن بزرگی حروف همگی تکراریاند، که سه تکراری حذفشده است. سه خط میمانند و به ترتیب الفبای انگلیسی بیرون میآیند:
Bread
Eggs
milk
| مقدار خواندهشده | مقدار |
|---|---|
| خطوط ورودی | ۷ |
| خطوط خروجی | ۳ |
| تکراریهای حذفشده | ۳ |
| خطوط خالی حذفشده | ۱ |
فایل دانلود shopping-list-lines.txt نام دارد — نام منبع با یک پسوند، نه دقیقاً نام منبع، چون دو فایل به نام shopping-list.txt در یک پوشهٔ دانلود همان راهی است که مرورگر یکی از آنها را بیسروصدا shopping-list (1).txt نامگذاری میکند.
اینها همان اعدادی هستند که در فایل آزمون این ابزار تأیید میشوند، پس اگر فرمول بدون تغییر این صفحه تغییر کند، ساخت شکست میخورد.
کاری که انجام نمیدهد
بر اساس هیچ چیز جز کل خط مرتب نمیکند. «مرتبسازی بر اساس ستون سوم»، «مرتبسازی بر اساس طول» و استخراج کلید وجود ندارد — برای آنها پاسخ واقعاً sort -k است. دو فایل را ادغام نمیکند، دو فایل را مقایسه نمیکند و خطوطی را که در یکی هست و در دیگری نیست پیدا نمیکند. گیومهگذاری CSV را نمیفهمد، پس فایل جداشده با ویرگولی که سلولهایش درون گیومه شکست خط دارند، مثل هر متن دیگری روی همان شکست خطها تقسیم میشود؛ اگر مهم است اول آن را تبدیل کنید. پایان خطهای اصلی یا نشانهٔ ترتیب بایت را حفظ نمیکند، و فایلی را که با UTF-۱۶ ذخیره شده یا اصلاً متن نیست پردازش نمیکند — هر دو با یک پیام رد میشوند، نه اینکه بیسروصدا خراب شوند.
بلوکهای زیرنویس را هم نمیفهمد. مرتب کردن آن خطوط دیالوگ را از زمانبندیاش جدا میکند؛ زمانبندی زیرنویس را با تجزیهگری جابهجا کنید که زمانهای شناختهشدهٔ هر بلوک را تغییر میدهد و متنشان را سر جایش میگذارد.
با هر خط مثل یک خط رفتار میکند. جایی که خطوط در واقع ردیفهایی با ستونهای درونیاند، CSV به JSON گیومهگذاری و ویرگولهای درون سلول را درست تجزیه میکند، نه اینکه روی اولین ویرگولی که میبیند تقسیم کند.
وقتی چند فهرست پاکیزهشده باید با هم منتقل شوند، از دانلودهای تمامشده یک فایل ZIP بسازید. روش Store بایتهای دقیق هر فایل متنی را نگه میدارد، در حالی که DEFLATE معمولاً متن تکراری را کوچک میکند و همچنان به هر فهرست نام فایل امن خودش را میدهد.
مراحل انجام کار
- فایل انتخابشده را در همین تب بخوانید، اگر UTF-۱۶ یا باینری است رد کنید، و بقیه را بهصورت UTF-۸ رمزگشایی کنید و نشانهٔ ترتیب بایت ابتدای فایل را دور بیندازید.
- متن را در بازگشت نورد (CR)، خطنو (LF) یا هر دو با هم به خطوط تقسیم کنید، تا فایل ویندوز، یونیکس یا مک کلاسیک همگی همان خطوط را بدهند. خطنوی پایانی، پایاندهندهٔ آخرین خط به حساب میآید، نه یک خط خالی پس از آن.
- اگر روشن است، فضای خالی ابتدا و انتهای هر خط را حذف کنید. این مرحله اول اجرا میشود، چون دو خطی که فقط در فاصلههای انتهایی فرق دارند برای خواننده یک خطاند.
- اگر روشن است، خطوطی را که چیزی جز فضای خالی ندارند حذف کنید. خطی از سه فاصله خالی به حساب میآید، چه حذف فاصلهها روشن باشد چه نباشد.
- خطوط تکراری را حذف کنید و اولین ظهور را نگه دارید. تطبیق بزرگی حروف را نادیده میگیرد، مگر اینکه «حساس به بزرگی حروف» روشن باشد که در آن صورت مقایسه دقیق است.
- خطوط را با الگوریتم همچینی یونیکد از طریق Intl.Collator، ثابتشده روی قواعد انگلیسی، از A تا Z یا از Z تا A مرتب کنید، یا ترتیب خود فایل را دستنخورده بگذارید.
- اگر روشن است، کل فهرست را در آخر معکوس کنید تا هر ترتیبی را که مرحلهٔ قبل ساخته برگرداند. خطوط را بهصورت UTF-۸ با یک خطنو پس از هر کدام بنویسید.
فرضها
- حذف فاصلهها پیش از حذف تکراریها و حذف تکراریها پیش از مرتبسازی اجرا میشود. این ترتیب را تغییر دهید و پاسخها عوض میشوند — «milk » و «milk» با حذف فاصلهها یک ورودیاند و بدون آن دو ورودی.
- خطوطی که دقیقاً برابر مقایسه میشوند ترتیبی را که در فایل داشتند نگه میدارند، چون هر مرتبسازی اینجا پایدار است. با نادیده گرفتن بزرگی حروف، «Milk» و «milk» برابرند، پس هر کدام اول آمده اول میماند.
- مرتبسازی صرفنظر از زبان صفحه از قواعد همچینی انگلیسی استفاده میکند، تا یک فایل با یک تنظیمات همیشه یک فایل تولید کند. زیر زبانهای محلی دیگر، چند حرف جای دیگری مینشستند — مثلاً در سوئدی Å پس از Z میآید، نه کنار A.
- تطبیق بدون حساسیت به بزرگی حروف از کوچکسازی یونیکد استفاده میکند، پس ß و SS همچنان خطوط متفاوتیاند. آکسانها همیشه تفاوتی واقعیاند — «résumé» و «resume» هرگز تکراری تلقی نمیشوند.
- کل فایل یکجا در حافظه نگه داشته میشود و به همین دلیل سقف آن ۲۰ مگابایت است. هر چیز بزرگتر به sort، awk یا یک پایگاه داده تعلق دارد، نه یک تب مرورگر.
- فایلی که برمیگردد UTF-۸ با پایان خط یونیکس و بدون نشانهٔ ترتیب بایت است، فایل ورودی هر چه بوده باشد.
پرسشهای رایج
آیا فهرستی که میدهم جایی آپلود میشود؟
خیر. فایل در همین تب مرورگر خوانده میشود و یک Worker روی دستگاه خودتان آن را پردازش میکند. سروری برای فرستادنش نیست، و اگر تا وقتی فایلی بارگذاری شده درخواستی از این سایت خارج شود، آزمون خودکار این صفحه شکست میخورد.
چرا وقتی «milk» از قبل در فهرست بود، «Milk» ناپدید شد؟
چون تطبیق بزرگی حروف را نادیده میگیرد، مگر اینکه «حساس به بزرگی حروف» را روشن کنید. فهرستی از نامها، آدرسها یا محصولات معمولاً میخواهد Milk و milk یک ورودی باشند، پس این پیشفرض است. «حساس به بزرگی حروف» را روشن کنید تا هر دو نگه داشته شوند؛ آنوقت جدا از هم هم مرتب میشوند.
چرا Zebra بعد از apple است، نه قبل از آن؟
چون خطوط بر اساس قواعد همچینی انگلیسی مرتب میشوند، نه بر اساس کد نویسهها. هر حرف بزرگ کد نویسهای کوچکتر از هر حرف کوچک دارد، پس ترتیب خام کدها Zebra، apple، banana میدهد — ترتیبی که رایانه میبیند و هیچکس نمیخواهد. همچینی همچنین Ångström را کنار Anchor میگذارد، نه بعد از Zulu.
چرا item۱۰ بعد از item۲ است، نه میان item۱ و item۲؟
چون «خواندن اعداد بهصورت عدد» بهطور پیشفرض روشن است، پس رشتهای از رقمها درون یک خط بهصورت عدد مقایسه میشود. برای ترتیب دقیق نویسهبهنویسه آن را در «گزینههای بیشتر» خاموش کنید، که item۱، item۱۰، item۲ میدهد.
چه چیزی خط خالی به حساب میآید؟
خطی که هیچ چیز ندارد، یا چیزی جز فاصله و تب ندارد. این تعریف چه حذف فاصلهها روشن باشد چه نباشد برقرار است، تا دو گزینه بر سر معنای «خالی» اختلاف نداشته باشند. خطی از سه فاصله با «حذف خطوط خالی» حذف میشود، حتی با حذف فاصلههای خاموش.
فایلم با پایان خط متفاوتی برگشت.
درست است. CRLF ویندوز و CR مک کلاسیک هر دو درست خوانده میشوند و آنچه نوشته میشود همیشه یک خطنوی تنها پس از هر خط دارد، با یکی در انتهای فایل. هر ویرایشگری که امروز استفاده میشود آن را باز میکند، از جمله Notepad.
چرا فایلم رد شد؟
دو چیز بهجای خراب شدن رد میشوند. فایلی که بهصورت UTF-۱۶ ذخیره شده با نشانهٔ ترتیب بایتی شروع میشود که این را میگوید، و رمزگشایی آن بهصورت UTF-۸ صفحهای از نویسههای جایگزین میسازد که به نظر میرسد ابزار فایل را خراب کرده — بهجای آن، فایل را با UTF-۸ دوباره ذخیره کنید. فایلی با یک بایت صفر نزدیک ابتدا باینری است نه متن، و اجرای ابزارهای خطی روی آن بیمعنی میشود.