파이썬은 텍스트 파일을 연결


168

와 같은 20 개의 파일 이름 목록이 ['file1.txt', 'file2.txt', ...]있습니다. 이 파일을 새 파일로 연결하는 Python 스크립트를 작성하고 싶습니다. 으로 각 파일을 f = open(...)열고을 호출하여 한 줄씩 읽고 f.readline()새 줄에 각 줄을 쓸 수 있습니다. 그것은 나에게 매우 "우아한"것처럼 보이지 않습니다. 특히 한 줄씩 읽거나 써야하는 부분입니다.

파이썬에서 이것을하는 더 "우아한"방법이 있습니까?


7
파이썬은 아니지만 쉘 스크립팅에서는 다음과 같은 작업을 수행 할 수 cat file1.txt file2.txt file3.txt ... > output.txt있습니다. 파이썬에서는 마음에 들지 않으면 readline()항상 readlines()또는 단순히 read()있습니다.
jedwards

1
@jedwards는 단순히 모듈을 cat file1.txt file2.txt file3.txt사용 하여 명령을 실행하면 subprocess완료됩니다. 그러나 catWindows에서 작동 하는지 확실하지 않습니다 .
Ashwini Chaudhary

5
참고로 설명하는 방식은 파일을 읽는 끔찍한 방법입니다. 사용 with하여 파일이 제대로 닫혀 수 있도록 문을 오히려 사용하는 것보다, 라인을 얻을 수있는 파일을 반복 f.readline().
Gareth Latty

텍스트 파일이 유니 코드 인 경우 @jedwards cat이 작동하지 않습니다.
Avi Cohen

답변:


258

이거해야 해

큰 파일의 경우 :

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for fname in filenames:
        with open(fname) as infile:
            for line in infile:
                outfile.write(line)

작은 파일의 경우 :

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for fname in filenames:
        with open(fname) as infile:
            outfile.write(infile.read())

… 그리고 내가 생각한 또 다른 흥미로운 것 :

filenames = ['file1.txt', 'file2.txt', ...]
with open('path/to/output/file', 'w') as outfile:
    for line in itertools.chain.from_iterable(itertools.imap(open, filnames)):
        outfile.write(line)

안타깝게도이 마지막 방법은 GC가 처리해야하는 열린 파일 디스크립터를 남겨 둡니다. 난 그냥 재미 있다고 생각


9
큰 파일의 경우 메모리가 매우 비효율적입니다.
Gareth Latty

1
@ inspectorG4dget : 나는 당신에게 묻지 않고, 당신의 솔루션이 효율적이지 않을 것이라고 불평하는 eyquem을 요구하고있었습니다. 나는 OP의 유스 케이스와 eyquem이 염두에 둔 모든 용도에 대해 충분히 효율적이라고 확신합니다. 그가 그렇지 않다고 생각한다면, 그것을 최적화하기를 요구하기 전에 그것을 증명하는 것은 그의 책임입니다.
abarnert

2
우리는 파일을 무엇으로 고려하고 있습니까?
Dee

4
@dee : 그것의 내용이 메인 메모리에 맞지 않는 큰 그래서 파일
inspectorG4dget

7
다시 말하면, 이것은 잘못된 대답입니다. shutil.copyfileobj가 정답입니다.
Paul Crowley

193

사용하십시오 shutil.copyfileobj.

그것은 당신을 위해 청크별로 입력 파일을 자동으로 읽습니다. 더 효율적이고 입력 파일을 읽는 것은 입력 파일 중 일부가 너무 커서 메모리에 맞지 않아도 작동합니다

import shutil

with open('output_file.txt','wb') as wfd:
    for f in ['seg1.txt','seg2.txt','seg3.txt']:
        with open(f,'rb') as fd:
            shutil.copyfileobj(fd, wfd)

2
for i in glob.glob(r'c:/Users/Desktop/folder/putty/*.txt'):글쎄 나는 디렉토리에 모든 파일을 포함시키기 위해 for 문을 교체했지만 output_file매우 빠른 시간에 100 기가 바이트처럼 정말로 커지기 시작했습니다.
R__raki__

10
EOL 문자가없는 경우 각 파일의 마지막 문자열을 다음 파일의 첫 번째 문자열과 병합합니다. 제 경우에는이 코드를 사용한 후 결과가 완전히 손상되었습니다. 나는 copyfileobj 뒤에 wfd.write (b "\ n")를 추가하여 정상적인 결과를 얻었습니다
Thelambofgoat

1
@ Thelambofgoat 나는 그 경우 순수한 연결이 아니라고 말하지만 당신의 필요에 맞는 것은 무엇이든 말할 것입니다.
HelloGoodbye

59

즉 무엇을 정확히 fileinput 함수 입니다 :

import fileinput
with open(outfilename, 'w') as fout, fileinput.input(filenames) as fin:
    for line in fin:
        fout.write(line)

이 유스 케이스의 경우 파일을 수동으로 반복하는 것보다 훨씬 간단하지 않지만 다른 경우에는 단일 파일처럼 모든 파일을 반복하는 단일 반복자를 갖는 것이 매우 편리합니다. (또한 fileinput각 파일이 완료 되 자마자 닫히게 된다는 사실은 필요 with하거나 각 파일이 필요하지 않다는 것을 의미 close하지만 이는 한 번의 비용 절감 일 뿐이며 큰 거래는 아닙니다.)

fileinput각 줄을 필터링하는 것만으로 파일을 적절하게 수정하는 기능과 같은 다른 유용한 기능 이 있습니다.


코멘트에 언급, 다른에서 설명하고있는 바와 같이 게시 , fileinput표시 파이썬 2.7에 대해 작동하지 않습니다. 코드를 파이썬 2.7과 호환되도록 약간 수정했습니다.

with open('outfilename', 'w') as fout:
    fin = fileinput.input(filenames)
    for line in fin:
        fout.write(line)
    fin.close()

@ Lattyware : 나는 배우는 대부분의 사람들 fileinput이 간단한 sys.argv(또는 optparse/ etc. 이후에 인수로 남은 것을 ) 사소한 스크립트를위한 큰 가상 파일 로 바꾸는 방법이라고 말하면서 아무것도 사용하지 않을 것이라고 생각합니다. 그렇지 않은 경우 (예 : 목록이 명령 줄 인수가 아닌 경우) 아니면 그들은 배우지 만 잊어 버립니다. 매년 2 ~ 2 년마다 다시 발견하고 있습니다.
abarnert

1
@abament for line in fileinput.input()이 특별한 경우에 선택하는 가장 좋은 방법은 아니라고 생각 합니다. OP는 파일을 한 줄씩 읽지 않고 이론적으로 실행하는 데 더 긴 프로세스 인 파일을 연결하려고합니다.
eyquem

1
@eyquem : 더 이상 실행 프로세스가 아닙니다. 스스로 지적했듯이 라인 기반 솔루션은 한 번에 한 문자 씩 읽지 않습니다. 그들은 청크를 읽고 버퍼에서 라인을 가져옵니다. I / O 시간은 라인 파싱 시간을 완전히 뒤흔들 것이므로 구현자가 버퍼링에서 멍청한 짓을하지 않는 한 빠르며 (아마도 좋은 버퍼를 추측하는 것보다 빠를 것입니다) 10000이 좋은 선택이라고 생각하면 스스로 크기를 정하십시오).
abarnert

1
@abarnert NO, 10000은 좋은 선택이 아닙니다. 2의 거듭 제곱이 아니며 엄청나게 작은 크기이기 때문에 실제로 매우 나쁜 선택입니다. 더 나은 크기는 2097152 (2 21), 16777216 (2 24) 또는 134217728 (2 ** 27) 일 것입니다.
eyquem

2
예제 코드는 Python 2.7.10 이상에서 유효하지 않습니다 : stackoverflow.com/questions/30835090/…
CnrL

8

나는 우아함에 대해 모른다. 그러나 이것은 효과가있다.

    import glob
    import os
    for f in glob.glob("file*.txt"):
         os.system("cat "+f+" >> OutFile.txt")

8
루프를 피할 수도 있습니다. import os; os.system ( "cat file * .txt >> OutFile.txt")
lib

6
크로스 플랫폼 그들에 공백이있는 파일 이름을 중단하지 않습니다
비행 양

3
이것은 안전하지 않습니다. 또한 cat파일 목록을 가져올 수 있으므로 반복해서 호출 할 필요가 없습니다. subprocess.check_call대신 에 전화하여 쉽게 안전하게 만들 수 있습니다os.system
Clément

5

UNIX 명령의 문제점은 무엇입니까? (Windows에서 작업하지 않는 경우) :

ls | xargs cat | tee output.txt 작업을 수행합니다 (원하는 경우 하위 프로세스로 파이썬에서 호출 할 수 있음)


21
이것은 파이썬에 관한 질문이기 때문입니다.
ObscureRobot

2
일반적으로 잘못된 것은 없지만이 답변은 깨졌습니다 (ls의 출력을 xargs로 전달하지 말고 파일 목록을 cat로 직접 전달하십시오 :) cat * | tee output.txt.
Clément

파일 이름을 삽입 할 수 있다면 좋을 것입니다.
Deqing

@Deqing 입력 파일 이름을 지정하려면 다음을 사용할 수 있습니다.cat file1.txt file2.txt | tee output.txt
GoTrained

1
... 그리고 1> /dev/null명령 끝에 추가하여 stdout (터미널에 인쇄)으로 전송을 비활성화 할 수 있습니다
GoTrained

4
outfile.write(infile.read()) # time: 2.1085190773010254s
shutil.copyfileobj(fd, wfd, 1024*1024*10) # time: 0.60599684715271s

간단한 벤치 마크는 셔틀의 성능이 더 우수하다는 것을 보여줍니다.


3

@ inspectorG4dget 답변에 대한 대안 (2016 년 3 월 29 일 현재 최고 답변). 436MB의 3 개 파일로 테스트했습니다.

@ inspectorG4dget 솔루션 : 162 초

다음 해결책 : 125 초

from subprocess import Popen
filenames = ['file1.txt', 'file2.txt', 'file3.txt']
fbatch = open('batch.bat','w')
str ="type "
for f in filenames:
    str+= f + " "
fbatch.write(str + " > file4results.txt")
fbatch.close()
p = Popen("batch.bat", cwd=r"Drive:\Path\to\folder")
stdout, stderr = p.communicate()

아이디어는 "오래된 좋은 기술"을 활용하여 배치 파일을 작성하고 실행하는 것입니다. 세미 파이썬이지만 더 빨리 작동합니다. 창에서 작동합니다.


3

디렉토리에 많은 파일이있는 경우 파일 glob2이름을 직접 작성하지 않고 파일 이름 목록을 생성하는 것이 더 좋습니다.

import glob2

filenames = glob2.glob('*.txt')  # list of all .txt files in the directory

with open('outfile.txt', 'w') as f:
    for file in filenames:
        with open(file) as infile:
            f.write(infile.read()+'\n')

2

File 객체의 .read () 메소드를 확인하십시오.

http://docs.python.org/2/tutorial/inputoutput.html#methods-of-file-objects

당신은 다음과 같은 것을 할 수 있습니다 :

concat = ""
for file in files:
    concat += open(file).read()

또는보다 '우아한'파이썬 방식 :

concat = ''.join([open(f).read() for f in files])

이 기사에 따르면 http://www.skymind.com/~ocrow/python_string/ 도 가장 빠릅니다.


10
이것은 파일의 크기에 따라 사용 가능한 메모리보다 클 수있는 거대한 문자열을 생성합니다. 파이썬은 파일에 대한 게으른 접근을 제공하기 때문에 나쁜 생각입니다.
Gareth Latty

2

파일이 거대하지 않은 경우 :

with open('newfile.txt','wb') as newf:
    for filename in list_of_files:
        with open(filename,'rb') as hf:
            newf.write(hf.read())
            # newf.write('\n\n\n')   if you want to introduce
            # some blank lines between the contents of the copied files

파일이 RAM에서 완전히 읽히고 유지되기에 너무 큰 경우, read(10000)예를 들어 , 고정 길이의 청크로 루프에서 복사 될 각 파일을 읽는 알고리즘은 약간 달라야합니다 .


@Lattyware 실행이 더 빠르기 때문에 확신합니다. 그런데 실제로 코드가 파일을 한 줄씩 읽도록 명령하더라도 파일은 청크로 읽히고 각 줄은 차례로 읽히는 캐시에 저장됩니다. 더 나은 절차는 읽기 청크 길이를 캐시 크기와 동일하게하는 것입니다. 그러나이 캐시의 크기를 결정하는 방법을 모르겠습니다.
eyquem

이것이 CPython의 구현이지만 그 중 어느 것도 보장되지 않습니다. 일부 시스템에서는 효과적 일 수 있지만 다른 시스템에서는 그렇지 않을 수 있으므로 그렇게 최적화하는 것은 좋지 않습니다.
Gareth Latty

1
물론, 라인 단위 판독은 버퍼링됩니다. 그것이 정확히 그렇게 느리지 않은 이유입니다. (실제로 파이썬을 플랫폼에 이식 한 사람이 10000보다 훨씬 나은 청크 크기를 선택했기 때문에 경우에 따라 약간 더 빠를 수도 있습니다.)이 성능이 실제로 중요한 경우 다른 구현을 프로파일 링해야합니다. 그러나 99.99… %의 시간 중 어느 쪽이든 속도가 충분히 빠르거나 실제 디스크 I / O가 느린 부분이므로 코드의 기능이 중요하지 않습니다.
abarnert

당신이 정말로 수동으로 버퍼링을 최적화해야 할 경우에도, 당신은 사용할 수 있습니다 os.openos.read때문에 일반 open수단 1 개 또는 2 여분의 버퍼가 당신의 방식으로 점점 C의 표준 입출력 주위에 사용 파이썬의 래퍼.
abarnert

추신 : 왜 10000이 나쁜지 : 파일은 아마도 디스크에 있고 아마도 바이트의 힘을 가진 블록이 있습니다. 그들이 4096 바이트라고 가정 해 봅시다. 따라서 10000 바이트를 읽는 것은 두 블록을 읽은 다음 다음 블록의 일부를 읽는 것을 의미합니다. 다른 10000을 읽는다는 것은 다음, 나머지 두 블록을 읽은 후 다음 블록의 일부를 읽는 것을 의미합니다. 부분 또는 전체 블록 읽기 수를 세어 보면 많은 시간을 낭비하고 있습니다. 다행스럽게도 Python, stdio, 파일 시스템 및 커널 버퍼링 및 캐싱은 이러한 문제의 대부분을 숨길 수 있지만 왜 먼저 문제를 만들려고합니까?
abarnert

0
def concatFiles():
    path = 'input/'
    files = os.listdir(path)
    for idx, infile in enumerate(files):
        print ("File #" + str(idx) + "  " + infile)
    concat = ''.join([open(path + f).read() for f in files])
    with open("output_concatFile.txt", "w") as fo:
        fo.write(path + concat)

if __name__ == "__main__":
    concatFiles()

-2
  import os
  files=os.listdir()
  print(files)
  print('#',tuple(files))
  name=input('Enter the inclusive file name: ')
  exten=input('Enter the type(extension): ')
  filename=name+'.'+exten
  output_file=open(filename,'w+')
  for i in files:
    print(i)
    j=files.index(i)
    f_j=open(i,'r')
    print(f_j.read())
    for x in f_j:
      outfile.write(x)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.