如何查找文件中特定行的字节位置

命令行查找文件中特定行的字节位置的最快方法是什么?

例如

$ 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 '!@##$@#%%^%&*%^&*(^)((**%%^@#' <<<'!@##$@#%%^%&*%^&*(^)((**%%^@#' !@##$@#%%^%&*%^&*(^)((**%%^@#