【问题标题】:Increasing numbers in strings by python or Tcl通过 python 或 Tcl 增加字符串中的数字
【发布时间】:2015-05-04 18:14:57
【问题描述】:

我有一些带有整数的字符串(一个字符串中有 2 到 5 个数字,用空格分隔)这是一个示例:

    1    4    5   19
    1    5
    2    3    6   59
    2    6
    3    2    4   60
    3    4
    4    1    3   61
    4    3
   25   13   23   64   65
   13   18
   14   13   15   75
   14   15
   15   14   16   76
   15   14
   45   44  102  103  104

我需要将所有数字重复增加 129,所以开头是:

130  133  134 148
130  134
131  132  135 188  ...

下一次增加将是:

259  262  263 277
259  263
260  261  264 317 ...

这种类型的字符串分析的最佳选择是什么? 首先计算数字,然后用“0”填充矩阵: [0, 0, 0, 0, 0] 比填充它 - 它将是: 第一行 [1, 4, 5, 19, 0] 第二行 [1, 5, 0, 0, 0]

并增加所有非零的数字。 我正在考虑以正确的方向解决此任务还是有更简单的方法? 还是有任何现成的解决方案,我只是不明白如何准确搜索它?

结果必须采用特定格式 - it is PDB file CONECT records

【问题讨论】:

  • 你的意思是什么分析?将非零值增加 129?这个规模问题不需要矩阵或数据框。只需将每一行的值添加到列表中。然后列出所有这些列表,然后将 129 添加到每个数字。
  • @marmeladze 是的,现在我意识到不需要创建空矩阵。并且使用 python 可以通过 .split() 创建矩阵

标签: python arrays string integer tcl


【解决方案1】:

一个Tcl,如果你只需要加起来的数字:

set text {    1    4    5   19
    1    5
    2    3    6   59
    2    6
    3    2    4   60
    3    4
    4    1    3   61
    4    3
   25   13   23   64   65
   13   18
   14   13   15   75
   14   15
   15   14   16   76
   15   14
   45   44  102  103  104}

proc addevery {txt amount} {
    # Creating an alias so we can modify the variable from outside the proc
    upvar text gtext
    set result [list]
    # Split to get lines
    foreach line [split $txt \n] {
        set lineresult [list]
        # Get each number added
        foreach item [regexp -all -inline {\S+} $line] {
            lappend lineresult [expr {$item+$amount}]
        }
        lappend result $lineresult
    }
    set gtext [join $result \n]
    puts $gtext
    return
}

puts "Adding 129:"
addevery $text 129

puts "\nAdding again 129:"
addevery $text 129

ideone demo


编辑:了解潜在问题后;我们必须保持格式(更具体地说,在每行数字系列之前添加CONECT,保持数字以 5 空格右缩进格式,并能够输出添加到原始数字的不同“步骤”同一个文件。最后一件事,第一次迭代实际上不应该向原始数字添加任何内容。

set fin [open "Input.txt" r]
set fout [open "Output.txt" w]

set lines [split [read $fin] "\n"]

# Amount to be added each time
set amount 129
# Number of times to be added
set times 100

proc addevery {amount} {
  global lines
  # Result list
  set lresult [list]
  foreach line $lines {
    # Result line
    set result {}
    # Get every 5 chars of the line
    foreach item [regexp -all -inline {.{5}} $line] {
      # Add, format then append to result line
      append result [format %5s [expr {[string trim $item]+$amount}]]
    }
    # Add line to the result list
    lappend lresult $result
  }
  # Set the lines to the new lines
  set lines $lresult
  return $lresult
}

for {set i 0} {$i < $times} {incr i} {
  # If 0, put the original with CONECT
  if {$i == 0} {
    puts $fout [join [lmap x $lines {set x "CONECT$x"}] "\n"]
  } else {
    puts $fout [join [lmap x [addevery $amount] {set x "CONECT$x"}] "\n"]
  }
}

close $fin
close $fout

作为奖励,python 等价物:

amount = 129
times = 100

import re

def addevery(amount):
  global lines
  lresult = []
  for line in lines:
    result = ''

    for item in re.findall(r'.{5}', line):
      result += "%5s" % (int(item.strip()) + amount)

    lresult.append(result)

  lines = list(lresult)
  return lresult

with open('Input.txt', 'r') as fin:
  with open('Output.txt', 'w') as fout:
    lines = fin.read().split('\n')
    for i in range(0,times):
      if i == 0:
        fout.write('\n'.join(['CONECT'+i for i in lines]) + '\n')
      else:
        fout.write('\n'.join(['CONECT'+i for i in addevery(amount)]) + '\n')

【讨论】:

    【解决方案2】:

    如果您知道最终尺寸,则可以预先分配一个 numpy 零数组。说出您最初希望如何处理每一行 (process_row),然后对文件中的每一行执行此操作 (process_file)。

    import numpy as np
    
    def process_row(row, row_num, out):
        row = row.split()
        nvals = len(row)
        out[row_num,:nvals] = row
    
    def process_file(fname, shape):
        data = np.zeros(shape)
        with open(fname) as fin:
            for i, row in enumerate(fin):
                process_row(row, i, data)
        return data
    
    data = process_file(fname="C:/temp/temp.txt", shape=(15,5))
    data[data != 0] += 129
    

    【讨论】:

      【解决方案3】:

      您正在寻找一个二维数组(这是一个矩阵)。

      首先,你可以用0来初始化它:

      Matrix = [[0 for x in range(20)] for x in range(5)]
      

      您必须更改所需尺寸的数字。 20是行数,5是列数。

      之后,您可以使用以下命令将数字放在您想要的位置:

      Matrix[r][c] = 1
      

      同样,R 是行,C 是列。

      如果你想在开始时填充矩阵,你也可以去:

      Matrix = [ [1, 4, 5, 19, 0], [1, 5, 0, 0, 0], [0, 0, 0, 0, 0],
      [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0] ]
      

      然后在彼此内部使用两个for循环来增加数字

      for i in range(20):
         for j in range(5):
            Matrix[i][j] = Matrix[i][j] + 129
      

      【讨论】:

        【解决方案4】:

        Tcl:lmap 需要 Tcl 8.6

        package require Tcl 8.6
        
        # list of strings
        set strings {
            {    1    4    5   19}
            {    1    5}
            {    2    3    6   59}
            {    2    6}
            {    3    2    4   60}
            {    3    4}
            {    4    1    3   61}
            {    4    3}
            {   25   13   23   64   65}
            {   13   18}
            {   14   13   15   75}
            {   14   15}
            {   15   14   16   76}
            {   15   14}
            {   45   44  102  103  104}
        }
        
        proc incr_all {listvar {n 1}} {
            upvar 1 $listvar lst
            set lst [lmap sublist $lst {lmap elem $sublist {expr {$elem + $n}}}]
        }
        
        proc format_strings {lst} {
            join [lmap sublist $lst {format [string repeat {%5s} [llength $sublist]] {*}$sublist}] \n
        }
        
        incr_all strings 119
        puts [format_strings $strings]
        

        输出

          120  123  124  138
          120  124
          121  122  125  178
          121  125
          122  121  123  179
          122  123
          123  120  122  180
          123  122
          144  132  142  183  184
          132  137
          133  132  134  194
          133  134
          134  133  135  195
          134  133
          164  163  221  222  223
        

        【讨论】:

        • 如何自动从行中获取 $strings 值?循环获取$input_file str($i),增加i值对吗?
        • 我会说:set fh [open file]; set strings [split [read -nonewline $fh] \n]; close $fh -- 这会为您提供文件行列表
        • 如何对所有数字进行格式化 - 在左侧用空格填充以获得长度 5?
        • 使用format 命令,正如我在回答中所展示的那样。
        • 当我从输入文件中读取所有行时:set fh [open file]; set strings [split [read -nonewline $fh] \n]; close $fh 我得到了 $strings 的这个值:{ 1 4 5 19} { 1 5} { 2 3 6 59} .... ,而不是 {{.
        【解决方案5】:

        几个 Tcl 解决方案。假设带有数字的原始文本在变量str中,见下文。

        一种方法是用命令调用替换所有数字来进行计算,然后对字符串执行替换(格式会有点偏差,因为数字现在会更宽但空格保持不变):

        set res [subst [regsub -all {\d+} $str {[expr {&+129}]}]]
        

        另一种方法是将字符串拆分成一个由行和数字组成的矩阵并遍历它:

        set res {}
        foreach line [split $str \n] {
            foreach n $line {
                append res [format %5s [incr n 129]]
            }
            append res \n
        }
        

        同样的方法使用 Tcl 8.6 lmap 映射命令:

        set res [join [lmap line [split $str \n] {
            join [lmap n $line {
                format %5s [incr n 129]
            }] {}
        }] \n]
        

        在这两种情况下,生成的字符串都将位于变量 res 中,并保留原始格式。

        ETA:右对齐输出:

        set res [join [lmap line [split $str \n] {
            format %25s [join [lmap n $line {
                format %5s [incr n 129]
            }] {}]
        }] \n]
        

        变量str 是这样分配的,作为纯文本(换行符在末端被修剪以避免空的重影元素):

        set str [string trim {
            1    4    5   19
            1    5
            2    3    6   59
            2    6
            3    2    4   60
            3    4
            4    1    3   61
            4    3
           25   13   23   64   65
           13   18
           14   13   15   75
           14   15
           15   14   16   76
           15   14
           45   44  102  103  104
        } \n]
        

        文档:append, expr, foreach, format, incr, lmap, regsub, set, split, string, string, p>

        【讨论】:

        • 矩阵似乎更容易,但如何更改字符串格式如下:“符号数 - 5,如果更少 - 在左侧添加空格”?
        【解决方案6】:
        set incriment 127
        set molecules 450
        set fout [open "Results.txt" w]
        close $fout
        
        proc addevery {filein fileout amount times} {
          set fh [open $filein r]
          set fout [open $fileout a+]
          while {[gets $fh line] != -1} {
            set result {}
            foreach item [regexp -all -inline {.{5}} $line] {
              append result [format %5s [expr {[string trim $item]+($amount*$times)}]]
            }
            puts $fout "CONECT$result"
          }
          close $fh
          close $fout
        }
        
        for {set i 0} {$i < $molecules} {incr i} {
            addevery "Connections_.txt" "Results.txt" $incriment $i
        }
        

        感谢https://stackoverflow.com/users/1578604/jerry 它正在运行,但尚未优化。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-06-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多