อ่าน n สุ่มบรรทัดจากไฟล์ขนาดใหญ่ที่อาจเกิดขึ้น


16

ความท้าทายนี้เกี่ยวกับการอ่านบรรทัดสุ่มจากไฟล์ขนาดใหญ่ที่อาจเกิดขึ้นโดยไม่ต้องอ่านไฟล์ทั้งหมดลงในหน่วยความจำ

อินพุต

จำนวนเต็มnและชื่อของไฟล์ข้อความ

เอาท์พุต

n บรรทัดของไฟล์ข้อความที่เลือกอย่างสุ่มโดยไม่ต้องเปลี่ยน

คุณสามารถสันนิษฐานได้ว่าnอยู่ในช่วง 1 ถึงจำนวนบรรทัดในไฟล์

ระวังเมื่อสุ่มตัวอย่างnตัวเลขโดยการสุ่มจากช่วงที่คำตอบที่คุณได้รับนั้นเหมือนกัน rand()%nใน C ไม่เหมือนกัน ทุกผลลัพธ์จะต้องมีโอกาสเท่าเทียมกัน

กฎและข้อ จำกัด

แต่ละบรรทัดของไฟล์ข้อความจะมีจำนวนอักขระเท่ากันและจะต้องไม่เกิน 80

รหัสของคุณจะต้องไม่อ่านเนื้อหาใด ๆ ของไฟล์ข้อความยกเว้น:

  • สายเหล่านั้นมันออกมา
  • บรรทัดแรกในการคำนวณจำนวนอักขระต่อบรรทัดในไฟล์ข้อความ

เราสามารถสมมติว่าตัวละครแต่ละตัวในไฟล์ข้อความใช้เวลาหนึ่งไบต์

ตัวแยกบรรทัดจะถือว่ามีความยาว 1 ไบต์ วิธีแก้ปัญหาอาจใช้ตัวคั่นบรรทัดยาว 2 ไบต์เฉพาะในกรณีที่พวกเขาระบุความต้องการนี้ คุณอาจจะสมมติว่าบรรทัดสุดท้ายถูกยกเลิกโดยตัวแยกบรรทัด

คำตอบของคุณควรเป็นโปรแกรมที่สมบูรณ์ แต่คุณสามารถระบุอินพุตในวิธีใดก็ได้ที่สะดวก

ภาษาและห้องสมุด

คุณสามารถใช้ภาษาหรือไลบรารีที่คุณต้องการ

หมายเหตุ

มีข้อกังวลเกี่ยวกับการคำนวณจำนวนบรรทัดในไฟล์ เมื่อ nimi ชี้ให้เห็นในความคิดเห็นคุณสามารถสรุปได้จากขนาดไฟล์และจำนวนตัวอักษรต่อบรรทัด

แรงจูงใจ

ในการแชทบางคนถามว่านี่เป็นคำถามที่ว่า "Do X ปราศจาก Y" หรือไม่ ฉันตีความสิ่งนี้เพื่อถามว่าข้อ จำกัด นั้นผิดปกติหรือไม่

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

shuf -n <num-lines>

อย่างไรก็ตามสิ่งนี้ช้ามากสำหรับไฟล์ขนาดใหญ่เนื่องจากอ่านได้ทั้งไฟล์


ทำไมต้องลงคะแนน?

3
นี่เป็นเรื่องเล็กน้อยในภาษาอย่าง C ที่มีfseekและเป็นไปไม่ได้ในผู้อื่น นอกจากนี้จะเกิดอะไรขึ้นถ้าnมากกว่าจำนวนบรรทัดในไฟล์
Mego

4
@Mego: เกี่ยวกับจุดของคุณ b): คุณสามารถคำนวณจำนวนบรรทัดโดยการหารขนาดไฟล์ด้วยความยาวของบรรทัด
nimi

8
Do X without Yเป็นคำเตือนที่ขึ้นต้นด้วย "ไม่เลวเสมอไป" ปัญหาหลักคือข้อ จำกัด เทียมเช่น "ไม่ได้ใช้ +" sum()ซึ่งให้ประโยชน์กับภาษาที่มี การไม่อ่านไฟล์ลงในหน่วยความจำเป็นข้อ จำกัด ที่ชัดเจนและสอดคล้องกัน สามารถทดสอบกับไฟล์ที่มีขนาดใหญ่กว่าหน่วยความจำซึ่งไม่สามารถแก้ไขได้ด้วยความแตกต่างของภาษา มันยังเกิดขึ้นกับแอปพลิเคชันในโลกแห่งความเป็นจริง (แม้ว่ามันไม่จำเป็นสำหรับกอล์ฟ ... )
trichoplax

1
ดูเหมือนว่านี่เป็นรหัสความซับซ้อนที่ จำกัด ซึ่งการใช้หน่วยความจำถูก จำกัด แม้ว่าไฟล์อาจมีขนาดใหญ่ มันไม่เกี่ยวกับการไม่มีบางสิ่งในรหัสของคุณ แต่มีข้อ จำกัด เกี่ยวกับวิธีที่รหัสอาจทำงาน
xnor

คำตอบ:


5

Dyalog APL , 63 ไบต์

⎕NREAD¨t 82l∘,¨lׯ1+⎕?(⎕NSIZE t)÷l←10⍳⍨⎕NREAD 83 80,⍨t←⍞⎕NTIE 0

พร้อมต์สำหรับชื่อไฟล์จากนั้นต้องการจำนวนบรรทัดสุ่มที่ต้องการ

คำอธิบาย

พรอมต์สำหรับการป้อนข้อความ (ชื่อไฟล์)
⎕NTIE 0ผูกไฟล์โดยใช้หมายเลขผูกที่มีอยู่ถัดไป (-1 ในระบบใหม่)
t←เก็บหมายเลขผูกที่เลือกไว้เป็นt
83 80,⍨ผนวกต่อท้าย [83,80] ให้ผลผลิต [-1,83,80]
⎕NREADอ่าน 80 ไบต์แรก ของไฟล์ -1 เป็นจำนวนเต็ม 8 บิต (รหัสการแปลง 83)
10⍳⍨ค้นหาดัชนีของหมายเลขแรก 10 (LF)
l←เก็บความยาวบรรทัดเป็นl
(⎕NSIZE t)÷หารขนาดไฟล์ -1 ด้วยความยาวบรรทัด
พร้อมรับคำป้อนตัวเลข (จำนวนบรรทัดที่ต้องการ ) การ
?สุ่มเลือก X (โดยไม่มีการแทนที่) ออกหมายเลขธรรมชาติ Y แรก
¯1+เพิ่ม -1 เพื่อรับหมายเลขบรรทัด 0 ต้นกำเนิด *
คูณด้วยความยาวบรรทัดเพื่อรับไบต์เริ่มต้น
t 82l∘,¨Prepend [-1,82, LineLength] ไปยังแต่ละไบต์เริ่มต้น (สร้าง รายการอาร์กิวเมนต์สำหรับ⎕NREAD)
⎕NREAD¨ อ่านแต่ละบรรทัดด้วยอักขระ 8 บิต (รหัสการแปลง 82)

ตัวอย่างการปฏิบัติ

ไฟล์ /tmp/records.txt ประกอบด้วย:

Hello
Think
12345
Klaus
Nilad

ทำให้โปรแกรม RandLines มีคำต่อคำโค้ดข้างต้นโดยการป้อนต่อไปนี้ในเซสชั่น APL:

∇RandLines
⎕NREAD¨t 82l∘,¨lׯ1+⎕?(⎕NSIZE t)÷l←10⍳⍨⎕NREAD 83 80,⍨t←⍞⎕NTIE 0
∇

ในประเภทเซสชัน APL RandLinesและกด Enter

ระบบเลื่อนเคอร์เซอร์ไปที่บรรทัดถัดไปซึ่งเป็นพรอมต์ความยาว 0 สำหรับข้อมูลอักขระ /tmp/records.txtเข้าสู่

ตอนนี้ระบบจะแสดงสัญญาณออก⎕:และรออินพุตเป็นตัวเลข 4เข้าสู่

ระบบเอาต์พุตสี่บรรทัดสุ่ม

ชีวิตจริง

ในความเป็นจริงคุณอาจต้องการให้ชื่อไฟล์และนับเป็นข้อโต้แย้งและรับผลเป็นตาราง สิ่งนี้สามารถทำได้โดยการป้อน:

RandLs←{↑⎕NREAD¨t 82l∘,¨lׯ1+⍺?(⎕NSIZE t)÷l←10⍳⍨⎕NREAD 83 80,⍨t←⍵⎕NTIE 0}

ตอนนี้คุณทำให้ MyLines มีสามบรรทัดสุ่มด้วย:

MyLines←3 RandLs'/tmp/records.txt'

วิธีการส่งคืนเพียงหนึ่งบรรทัดสุ่มถ้าไม่นับจำนวน:

RandL←{⍺←1 ⋄ ↑⎕NREAD¨t 82l∘,¨lׯ1+⍺?(⎕NSIZE t)÷l←10⍳⍨⎕NREAD 83 80,⍨t←⍵⎕NTIE 0}

ตอนนี้คุณสามารถทำได้ทั้งสองอย่าง:

MyLines←2 RandL'/tmp/records.txt'

และ (สังเกตเห็นว่าไม่มีอาร์กิวเมนต์ซ้าย):

MyLine←RandL'/tmp/records.txt'

ทำให้รหัสสามารถอ่านได้

APL หนึ่งรายที่เล่นกอล์ฟเป็นความคิดที่ไม่ดี นี่คือวิธีที่ฉันจะเขียนในระบบการผลิต:

RandL←{ ⍝ Read X random lines from file Y without reading entire file
    ⍺←1 ⍝ default count
    tie←⍵⎕NTIE 0 ⍝ tie file
    length←10⍳⍨⎕NREAD 83 80,⍨tie ⍝ find first NL
    size←⎕NSIZE tie ⍝ total file length
    starts←lengthׯ1+⍺?size÷length ⍝ beginning of each line
    ↑⎕NREAD¨tie 82length∘,¨starts ⍝ read each line as character and convert list to table
}

* ฉันจะสามารถประหยัดไบต์โดยการทำงานในโหมด 0 แหล่งกำเนิดซึ่งเป็นมาตรฐานในระบบ APL บาง: ถอด¯1+และใส่ก่อน1+10


อ๊ะ .. APL :) มีวิธีทดสอบโค้ดนี้ใน linux ไหม?

@ Lembik แน่นอนรหัสนี้เป็นแพลตฟอร์มข้าม ดาวน์โหลดจาก dyalog.com
อดัม

เมื่อฉันไม่อ่าน APL คุณสามารถอธิบายรหัสได้หรือไม่ ส่วนที่ยุ่งยากคือการสุ่มตัวอย่างบรรทัดโดยไม่มีการแทนที่และกระโดดไปยังตำแหน่งที่ถูกต้องในไฟล์เพื่ออ่านบรรทัด

@ Lembik ส่วนนั้นเป็นเรื่องง่าย อาร์กิวเมนต์ของ RENREAD คือ TieNumber ConversionCode BytesToRead [StartByte] มันอ่านเพียงไบต์ที่จำเป็น ที่เหลือก็แค่หาว่าจะอ่านอะไร
อดัม

@ Lembik ฉันสงสัยว่าทำไมคำตอบของฉันไม่ชนะรางวัล
อดัม

7

Ruby, 104 94 92 90 ไบต์

ชื่อไฟล์และจำนวนบรรทัดจะถูกส่งผ่านไปยังบรรทัดคำสั่ง ตัวอย่างเช่นหากโปรแกรมเป็นshuffle.rbและชื่อไฟล์a.txtให้รันruby shuffle.rb a.txt 3เป็นสามบรรทัดสุ่ม

-4 ไบต์จากการค้นพบopenไวยากรณ์ใน Ruby แทนFile.new

f=open$*[0]
puts [*0..f.size/n=f.gets.size+1].sample($*[1].to_i).map{|e|f.seek n*e;f.gets}

นอกจากนี้ยังมีวิธีแก้ปัญหาฟังก์ชั่นแบบไม่ระบุชื่อขนาด 85 ไบต์ซึ่งรับค่าสตริงและตัวเลขเป็นอาร์กิวเมนต์

->f,l{f=open f;puts [*0..f.size/n=f.gets.size+1].sample(l).map{|e|f.seek n*e;f.gets}}

ต่ำกว่า 100 ไบต์! บางที Ruby อาจเป็นภาษาที่ดีที่สุดในการเล่นกอล์ฟ 'ตัวอย่าง' หลีกเลี่ยงการทำซ้ำหรือไม่

@Lembik ruby-doc.org/core-2.2.0/Array.html#method-i-sampleมันหลีกเลี่ยงการทำซ้ำ อย่าบอกฉัน ... ฉันควรจะทำซ้ำหรือไม่?
หมึกมูลค่า

ไม่มีคุณสมบูรณ์แบบ :)

คุณสามารถบันทึกไบต์ใด ๆ ด้วยการอ่านจาก stdin หรือไม่ ruby shuffle.rb 3 < a.txtช่วยให้คุณค้นหา stdin IDK Ruby
Peter Cordes

1
@PeterCordes มันสมเหตุสมผล แต่ดังที่กล่าวไปแล้วจุดของความล้มเหลวคือ Ruby ไม่สามารถอ่านขนาดไฟล์ของ stdin ได้ดังนั้นจึงไม่ได้ผล
หมึกมูลค่า

5

Haskell, 240 224 236 ไบต์

import Test.QuickCheck
import System.IO
g=hGetLine
main=do;f<-getLine;n<-readLn;h<-openFile f ReadMode;l<-(\x->1+sum[1|_<-x])<$>g h;s<-hFileSize h;generate(shuffle[0..div s l-1])>>=mapM(\p->hSeek h(toEnum 0)(l*p)>>g h>>=putStrLn).take n

อ่านชื่อไฟล์และ n จาก stdin

มันทำงานอย่างไร:

main=do
  f<-getLine                   -- read file name from stdin
  n<-readLn                    -- read n from stdin
  h<-openFile f ReadMode       -- open the file
  l<-(\x->1+sum[1|_<-x])<$>g h -- read first line and bind l to it's length +1
                               -- sum[1|_<-x] is a custom length function
                               -- because of type restrictions, otherwise I'd have
                               -- to use "toInteger.length"
  s<-hFileSize h               -- get file size
  generate(shuffle[0..div s l-1])>>=
                               -- shuffle all possible line numbers 
  mapM (\->p  ...  ).take n    -- for each of the first n shuffled line numbers 
     hSeek h(toEnum 0).(l*p)>> -- jump to that line ("toEnum 0" is short for "AbsoluteSeek")
     g h>>=                    -- read a line from current position
     putStrLn                  -- and print

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

แก้ไข: ฉันพลาดส่วน "สุ่มโดยไม่มีการแทนที่" (ขอบคุณ @feersum สำหรับการสังเกต!)


Haskell

1
จะหลีกเลี่ยงการเลือกบรรทัดที่เลือกไว้แล้วได้อย่างไร
feersum

@feersum: โอ้ฉันพลาดส่วนนั้นไป แก้ไขแล้ว.
nimi

ฉันเห็นstackoverflow.com/questions/13779630/…ค่อนข้างละเอียด!

1
อาจจะมีความท้าทายแยกต่างหากในการสุ่มตัวอย่างโดยไม่มีการแทนที่ในพื้นที่ขนาดเล็ก

3

PowerShell v2 +, 209 ไบต์

param($a,$n)
$f=New-Object System.IO.FileStream $a,"Open"
for(;$f.ReadByte()-ne10){$l++}
$t=$f.Length/++$l-1
[byte[]]$z=,0*$l
0..$t|Get-Random -c $n|%{$a=$f.Seek($l*$_,0);$a=$f.Read($z,0,$l-1);-join[char[]]$z}

รับอินพุต $aเป็นชื่อไฟล์และ$nเป็นจำนวนบรรทัด โปรดทราบว่า$aจะต้องเป็นชื่อไฟล์แบบเต็มพา ธ และถือว่าเป็นการเข้ารหัส ANSI

จากนั้นเราจะสร้างใหม่ FileStreamวัตถุและบอกให้เข้าถึงไฟล์$aด้วยOpenสิทธิ์

forห่วง.Read()ผ่านบรรทัดแรกจนกระทั่งเราตี\nตัวละครเพิ่มตัวนับความยาวบรรทัดของเราแต่ละตัว จากนั้นเราตั้งค่า$tเท่ากับขนาดของไฟล์ (เช่นระยะเวลาที่สตรีม) หารด้วยจำนวนอักขระต่อบรรทัด (บวกหนึ่งดังนั้นจึงนับจำนวนเทอร์มิเนเตอร์) ลบหนึ่ง (เนื่องจากเราไม่มีดัชนี) จากนั้นเราสร้างบัฟเฟอร์ของเรา$zให้มีความยาวบรรทัด

บรรทัดสุดท้ายสร้างอาร์เรย์แบบไดนามิกด้วยตัว..ดำเนินการช่วง 1เราไปป์นั้นGet-Randomด้วย-Cจำนวน ount ของ$nการสุ่มเลือก$nหมายเลขบรรทัดโดยไม่ต้องทำซ้ำ |%{...}หมายเลขที่โชคดีได้รับการประปาเข้าไปในวงด้วย การวนซ้ำแต่ละครั้งเรา.Seekไปยังสถานที่เฉพาะแล้ว.Readเก็บเป็นอักขระ$zในมูลค่าของบรรทัดของตัวอักษรเก็บไว้ในเรา re-cast $zเป็น char-array และ-joinรวมเข้าด้วยกันโดยปล่อยสตริงผลลัพธ์บนไพพ์ไลน์

ในทางเทคนิคเราควรลงท้ายด้วยการ$f.Close()โทรเพื่อปิดไฟล์ แต่ค่าใช้จ่ายไบต์! : p

ตัวอย่าง

a.txt:
a0000000000000000000000000000000000000000000000001
a0000000000000000000000000000000000000000000000002
a0000000000000000000000000000000000000000000000003
a0000000000000000000000000000000000000000000000004
a0000000000000000000000000000000000000000000000005
a0000000000000000000000000000000000000000000000006
a0000000000000000000000000000000000000000000000007
a0000000000000000000000000000000000000000000000008
a0000000000000000000000000000000000000000000000009
a0000000000000000000000000000000000000000000000010

PS C:\Tools\Scripts\golfing> .\read-n-random-lines.ps1 "c:\tools\scripts\golfing\a.txt" 5
a0000000000000000000000000000000000000000000000002 
a0000000000000000000000000000000000000000000000001 
a0000000000000000000000000000000000000000000000004 
a0000000000000000000000000000000000000000000000010 
a0000000000000000000000000000000000000000000000006 

1 ในทางเทคนิคหมายความว่าเราสามารถรองรับได้สูงสุด 50,000 บรรทัดเท่านั้นเนื่องจากเป็นช่วงที่ใหญ่ที่สุดที่สามารถสร้างได้ในลักษณะนี้ : - / แต่เราไม่สามารถวนรอบGet-Randomคำสั่งเพียง$nครั้งเดียวยกเลิกการทำซ้ำแต่ละวงเนื่องจากไม่ได้กำหนดไว้ ...


2

Python 3, 146 139 bytes

from random import*
i=input
f=open(i())
l=len(f.readline())
[(f.seek(v*l),print(f.read(l)))for v in sample(range(f.seek(0,2)//l),int(i()))]
#print is here^

อินพุต: [ชื่อไฟล์] \ n [บรรทัด] \ n

วิธีนี้จะขโมยอย่างมากจาก @pppery แต่เป็น python3.5 เท่านั้นเป็นโปรแกรมที่สมบูรณ์

แก้ไข: ขอบคุณ @Mego สำหรับช่วงอินไลน์และความเข้ากันได้ของ python3.x แก้ไข 2: คำอธิบายที่พิมพ์เป็นเพราะฉันได้รับสองความคิดเห็นเกี่ยวกับมัน (ความเห็นไม่ชัดว่าเป็นส่วนหนึ่งของรหัสหรือจำนวนไบต์)


ขอขอบคุณ! ส่วนใดของงูหลาม 3.5 เท่านั้น?

2
r=range(f.seek(0,2)//l)จะทำงานได้ซึ่งจะลบออก 3 ไบต์และลบความต้องการ 3.5 ยิ่งกว่านั้นให้โกนทิ้งอีก 3 ไบต์ด้วยการขีดเส้นrangeในการsampleโทร นอกจากนี้นี่ไม่ใช่โปรแกรมที่สมบูรณ์ - คุณต้องพิมพ์รายการจริง
Mego

@ Lembik: มันเป็นเพียง 3.5 เพราะฉันใช้r=[*range(f.seek(0,2)//l)]เพราะฉันคิดว่าฉันไม่สามารถsampleสร้าง ปรากฎว่าฉันทำได้ @ Mega: มันเสร็จสมบูรณ์เพราะมันพิมพ์ทุกบรรทัดภายในรายการความเข้าใจprint(f.read(l))
Alexander Nigl

คุณจำเป็นต้องมีคำสั่งพิมพ์

การพิมพ์อยู่ในรายการความเข้าใจ
Alexander Nigl

2

ลัวะ126 122

r=io.read;f=io.open(r())c=2+f:read():len()for i=1,r()do f:seek("set",c*math.random(0,f:seek("end")/c-1))print(f:read())end

ใช้ 2 ไบต์สำหรับการขึ้นบรรทัดใหม่ เปลี่ยน 2 เป็น 1 สำหรับ 1 ฉันมีเพียงแค่ 2 เพราะนั่นคือสิ่งที่ไฟล์ทดสอบของฉันมี

มีตัวเองภายใต้รายการ PHP แต่ยังคงเป็นที่ 2 (ปัจจุบัน) สาปแช่งคุณรายการทับทิม!


1
Lua เป็นภาษาการเขียนโปรแกรมแรกที่ฉันได้เรียนรู้และแม้แต่กับสิ่งที่ฉันได้เรียนรู้ตั้งแต่นั้นมามันก็ยังเป็นที่ฉันโปรดปราน มันมีประโยชน์หลายอย่างเพื่อความสะดวกในการเขียน
Blab

2

Bash (ดี coreutils) 100 ไบต์

n=`head -1 $2|wc -c`;shuf -i0-$[`stat -c%s $2`/$n] -n$1|xargs -i dd if=$2 bs=$n skip={} count=1 2>&-

คำอธิบาย

วิธีนี้หลีกเลี่ยงการอ่านไฟล์ทั้งหมดโดยใช้ ddเพื่อแยกส่วนของไฟล์ที่เราต้องการโดยไม่ต้องอ่านไฟล์ทั้งหมด แต่น่าเสียดายที่มันมีขนาดใหญ่มากพร้อมตัวเลือกทั้งหมดที่เราต้องระบุ:

ifคือไฟล์อินพุต
bsคือขนาดบล็อก (ที่นี่เราตั้งค่า$nซึ่งความยาวของบรรทัดแรก
skipถูกตั้งค่าเป็นจำนวนเต็มแบบสุ่มที่แยกออกมาshufและเท่ากับibsค่าที่ข้ามskip* ibsไบต์
countจำนวนibsส่วนความยาวที่จะส่งกลับ
status=noneจะต้องตัดออก ข้อมูลปกติส่งออกโดยdd

เราได้รับความยาวสายโดยใช้และขนาดไฟล์ที่มีhead -1 $2|wc -cstat -c%s $2

การใช้

บันทึกข้างต้นและเรียกใช้file.shfile.sh n filename

การกำหนดเวลา

time ~/randlines.sh 4 test.txt
9412647
4124435
7401105
1132619

real    0m0.125s
user    0m0.035s
sys     0m0.061s

เมื่อเทียบกับ

time shuf -n4 test.txt
1204350
3496441
3472713
3985479

real    0m1.280s
user    0m0.287s
sys     0m0.272s

ไทม์ข้างต้นสำหรับไฟล์ 68MiB seq 1000000 9999999 > test.txtสร้างขึ้นโดยใช้

ขอบคุณ@PeterCordesสำหรับเคล็ดลับ -1 ของเขา!


1
ฉันชอบคำตอบ bash เสมอ แต่คุณช่วยอธิบายได้ไหมว่าวิธีนี้ไม่ได้อ่านไฟล์ทั้งหมด?

2
@Lembik เพิ่มคำอธิบายแล้ว!
Dom Hastings

1
คุณสามารถbs=แทนibs=เนื่องจากการตั้งค่าobsเช่นกันเป็นเรื่องปกติ ฉันคิดว่าคุณไม่สามารถแทนที่if=$2ด้วย<$2แม้ว่าเนื่องจากยังคงxargsเป็นบรรทัดคำสั่งของ \<$2ไม่ทำงาน (xargs ใช้ exec โดยตรงโดยไม่มีเชลล์)
Peter Cordes

ฉันหวังว่ามันจะไม่มากเกินไป แต่ฉันชอบคำตอบนี้ :) ลองทดสอบด้วยไฟล์ 1GB

1
เรื่องการเปลี่ยนเส้นทาง stderr ไปยัง stdin: คุณสามารถปิด stderr ด้วย2>&-ดังนั้นจึงไม่มีอันตรายจากการส่งออกไปทุกที่ (เช่นถ้า stdin เกิดขึ้นเป็น descriptor ไฟล์อ่าน - เขียน) จะทำงานร่วมกับ GNU dd: มันก็ยังคงผลิตของตนก่อนที่จะพยายามและความล้มเหลวที่จะเขียนถึงstdout stderr
Peter Cordes

1

Python 3 - 161 160 149 ไบต์

from random import*;
def f(n,g):f=open(g);l=len(f.readline());r=list(range(f.seek(0,2)/l));shuffle(r);[(f.seek(v*l),print(f.read(l)))for v in r[:k]]

รหัสนี้กำหนดฟังก์ชั่นที่เรียกว่าชอบ f(10,'input.txt')


1
ความท้าทายต้องใช้โปรแกรมเต็มรูปแบบดังนั้นฉันจึงกลัวว่านิยามฟังก์ชั่นไม่เพียงพอ
nimi

หากต้องการบันทึกไบต์ลบพื้นที่ระหว่างการนำเข้าและ *
mriklojn

1
@nimi ต้องการโปรแกรมเต็มรูปแบบสำหรับความท้าทายนี้ดูเหมือนว่าจะมีอำนาจเหนือกฎการจัดรูปแบบโค้ดเริ่มต้นโดย
พลการ

@ppperry: ใช่บางที แต่นั่นเป็นเพียงวิธีการ
nimi

ในการรับความยาวของไฟล์คุณสามารถf.seek (0,2)ซึ่งทำให้การนำเข้าระบบปฏิบัติการและ os.stat ล้าสมัย
Alexander Nigl

1

C # 259 ไบต์โดยไม่ซ้ำกัน

class Program{static void Main(string[]a){int c=Convert.ToInt32(a[1]);var h=File.ReadLines(a[0]);HashSet<int>n=new HashSet<int>();while(n.Count<c)n.Add(new Random().Next(0,h.Count()));for(;c>0;c--)Console.WriteLine(h.Skip(n.ElementAt(c-1)).Take(1).First());}}

Ungolfed

class Program{static void Main(string[] a)
{
        int c = Convert.ToInt32(a[1]);
        var h = File.ReadLines(a[0]);
        HashSet<int> n = new HashSet<int>();
        while (n.Count < c)
            n.Add(new Random().Next(0, h.Count()));           
        for (; c > 0; c--)
            Console.WriteLine(h.Skip(n.ElementAt(c-1)).Take(1).First());
    }
}

File.ReadLines คือ Lazy นี่คือข้อดีเพิ่มเติมที่ทุกบรรทัดสามารถมีความยาวต่างกัน

ใช้มันจะเป็น:

sample.exe a.txt 10000

C # 206 ไบต์ด้วยซ้ำ

class Program{static void Main(string[]a){var n=new Random();int c=Convert.ToInt32(a[1]);var h=File.ReadLines(a[0]);for(;c>0;c--)Console.WriteLine(h.Skip((int)(n.NextDouble()*h.Count())).Take(1).First());}}

Ungolfed

class Program
{
    static void Main(string[] a)
    {
        Random n = new Random();
        int c = Convert.ToInt32(a[1]);
        var h = File.ReadLines(a[0]);
        for (; c > 0; c--)
            Console.WriteLine(h.Skip((int)(n.NextDouble()*h.Count())).Take(1).First());
    }
}

ฉันไม่ทำตามวิธีแก้ปัญหาของคุณอย่างเต็มที่ หากทุกบรรทัดมีความยาวต่างกันงานนั้นจะเป็นไปไม่ได้ นอกจากนี้คุณจะสุ่มตัวอย่างบรรทัดโดยไม่มีการแทนที่ได้อย่างไร ฉันขอโทษ C # ของฉันไม่ดีพอ

@ Lembik คุณพูดถูกฉันไม่ได้คิดถึงเรื่องซ้ำซ้อน และฉันสามารถนับจำนวนบรรทัดและแยกบรรทัดด้วย linenumber ซึ่งเป็นสาเหตุที่เส้นอาจแปรผันได้
Master117

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

@Lembik File.ReadLines ("pathToFile") สร้าง Lazy enumeration บนทุกบรรทัดของไฟล์ File.ReadLines ("pathToFile") elementAt (19) ส่งคืนบรรทัดที่ 19 ของไฟล์ ค่อนข้างเหมือนแผนที่ของ Linestarts ทั้งหมด
Master117

ฉันไม่คิดว่า Lazy enumeration jumps (หรือค้นหา) ในไฟล์เศร้า ดังนั้นจึงไม่สอดคล้องกับกฎในปัจจุบัน

1

Python (141 ไบต์)

เก็บแต่ละบรรทัดด้วยความน่าจะเป็นที่เท่ากันใช้กับท่อด้วย ไม่ตอบข้ามข้อ จำกัด ล่วงหน้าของคำถามแม้ว่า ...

การใช้งานcat largefile | python randxlines.py 100หรือpython randxlines 100 < largefile(ตามที่ @ petercordes ชี้ให้เห็น)

import random,sys
N=int(sys.argv[1])
x=['']*N
for c,L in enumerate(sys.stdin):
    t=random.randrange(c+1)
    if(t<N):x[t] = L
print("".join(x))

3
ประเด็นทั้งหมดของคำถามนี้คือคุณต้องค้นหาในอินพุตสตรีม คุณอาจพูดได้ว่านั่นเป็นส่วนหนึ่งของข้อ จำกัด ของคำถามที่คุณไม่สนใจ (แม้ว่าการใช้ตัวอย่าง read-from-a-pipe จะค่อนข้างชัดเจน) การอ่านจาก stdin ด้วยpython ./randxlines.py 100 < largefileจะดีสำหรับคำตอบที่เหมาะสมแม้ว่า: ในกรณีstdinนั้นจะหาได้
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.