从命令行查找文件中特定行的字节位置的最快方法是什么?
例如
$ linepos myfile.txt 13 5283
我正在编写一个大小为几GB的CSVparsing器,如果parsing器停止,我希望能够从最后一个位置恢复。 parsing器是用Python file.readlines()
,但是即使迭代file.readlines()
需要很长时间,因为文件中有数百万行。 我想简单地做file.seek(int(command.getoutput("linepos myfile.txt %i" % lastrow)))
,但我找不到一个shell命令来有效地做到这一点。
编辑:抱歉的困惑,但我正在寻找一个非Python的解决scheme。 我已经知道如何从Python做到这一点。
从@ chepner的评论我的其他答案:
position = 0 # or wherever you left off last time try: with open('myfile.txt') as file: file.seek(position) # zero in base case for line in file: position = file.tell() # current seek position in file # process the line except: print 'exception occurred at position {}'.format(position) raise
遍历文件对象会产生完整的行结尾的行。 您应该能够将len
添加到一个counter对象来获取位置。 您需要根据字符编码(字符字节大小)
position = 0 # or wherever you left off last time try: with open('myfile.txt') as file: # don't you go correcting me on naming it file. we don't call file directly anyway! file.seek(position) # zero in base case for line in file: position += len(line) # process the line except: # yes, a naked exception. TWO faux pas in one answer?!? print 'exception occurred at position {}'.format(position) raise # re-raise to see traceback or what have you
那么,如果你的模式很简单,这将是简单的
$ echo -e '#!/bin/bash\necho abracadabra' >/tmp/script $ pattern=bash $ sed -rn "0,/$pattern/ {s/^(.*)$pattern.*$/\1/p ;t exit; p; :exit }" /tmp/script \ | wc -c 8
正如你所看到的,假设文件中第一个字符的数字为1,这将输出模式中第一个字符的位置。
注意1: sed
习惯在最后一个字符串中添加一个尾随的换行符,因此,当我们取出pattern
前面的一部分行时,输出中的字节数应该是7(计数它们→ #!/bin/
),但什么wc -c
实际上计数看起来像
$ sed -rn "0,/$pattern/ {s/^(.*)$pattern.*$/\1/p ;t exit; p; :exit }" /tmp/script \ | hexdump -C 00000000 23 21 2f 62 69 6e 2f 0a |#!/bin/.| 00000008
例如,如果您正在寻找EOF,这可能是潜在的麻烦来源。 我想不出一个更合适的案例,我只想指出。
注意2:如果模式将包含特殊字符,sed将失败。 如果你能提供一个你正在寻找的例子,我可以逃避它。
注意3:这假定pattern
是独特的。 如果您将停止读取pattern
的第二个或第三个实例的文件,这将无法正常工作。
更新。 我找到了一个更简单的方法。
$ grep -bo bash <<< '#!/bin/bash' 7:bash
对于GNU grep,有两个选择:
-b, --byte-offset Print the 0-based byte offset within the input file before each line of output. If -o (--only-matching) is specified, print the offset of the matching part itself.
我建议使用grep,因为如果你指定-F
key,它会把pattern作为一个简单的字符串。
$ grep -F '!@##$@#%%^%&*%^&*(^)((**%%^@#' <<<'!@##$@#%%^%&*%^&*(^)((**%%^@#' !@##$@#%%^%&*%^&*(^)((**%%^@#