Bổ sung số lượng cực lớn trong tập lệnh shell


8

Giả sử rằng hai số được lưu trữ trong hai tệp khác nhau a.txtb.txt.

Mỗi số đủ lớn (hơn 30 chữ số) để không được hỗ trợ bởi loại dữ liệu số được sử dụng bởi bash.

Làm thế nào tôi có thể thêm chúng trong vỏ?


Cá nhân tôi sẽ sử dụng pythonhoặc tương tự trong trường hợp đó.
phk

Chắc chắn bạn không muốn sử dụng sed để thay thế?
Jeff Schaller

Cách đây một thời gian, trong lớp java của tôi, chúng tôi đã sử dụng các ngăn xếp để thêm các số nằm ngoài phạm vi tối đa của java. Giả sử bạn sẵn sàng gặp rắc rối khi triển khai ngăn xếp bằng cách sử dụng mảng trong bash, bạn có thể làm điều đó. . . nhưng nó rất dư thừa. . . và không cần thiết như bạn có thể thấy từ các câu trả lời dưới đây. Hoặc chỉ sử dụng pythonnhư phk đề xuất
Sergiy Kolodyazhnyy

Câu trả lời:


12

Giả sử chúng là số thập phân, bạn có thể làm:

paste -d + a.txt b.txt | bc

Coi chừng bcdòng đó kết thúc các số rất dài (hơn 68 hoặc 69 chữ số tùy theo việc thực hiện). Với GNU bc, bạn có thể vô hiệu hóa nó bằng cách đặt BC_LINE_LENGTHbiến môi trường thành 0, như với:

paste -d + a.txt b.txt | BC_LINE_LENGTH=0 bc

10

Bí quyết là không sử dụng bashđể thực hiện phép cộng 1 .

Đầu tiên, đọc mỗi số thành một biến riêng biệt. Điều này giả định rằng các tệp chỉ chứa một số và không có thông tin khác.

a="$(<a.txt)"
b="$(<b.txt)"

Sau đó sử dụng bcmáy tính để nhận kết quả:

bc <<<"$a + $b"

bc là một "ngôn ngữ và máy tính số học chính xác tùy ý".

Để lưu trữ kết quả trong một biến c:

c="$( bc <<<"$a + $b" )"

Nếu <<<cú pháp cảm thấy kỳ lạ (nó được gọi là "chuỗi ở đây" và là một phần mở rộng cho cú pháp shell POSIX được hỗ trợ bởi bashvà một vài shell khác), thay vào đó bạn có thể sử dụng printfđể gửi bổ sung tới bc:

printf '%s + %s\n' "$a" "$b" | bc

Và lưu trữ kết quả trong cmột lần nữa:

c="$( printf '%s + %s\n' "$a" "$b" | bc )"

1 Sử dụng bashđể thực hiện việc cộng hai số cực lớn sẽ đòi hỏi phải thực hiện, trong bashkịch bản, một thói quen để thực hiện số học chính xác tùy ý . Điều này là hoàn toàn có thể thực hiện được, nhưng cồng kềnh và không cần thiết vì mọi Unix đi kèm bcđã cung cấp dịch vụ này cho bạn theo cách tương đối dễ dàng và dễ tiếp cận.


1
Ngoài ra, bạn có thể làm read a < a.txt. Điều đó cũng sẽ quan tâm đến việc tước bỏ khoảng trống hàng đầu và dấu vết nếu có (giả sử $IFSchưa được sửa đổi).
Stéphane Chazelas

1
Tại sao các trích dẫn bên trong các trích dẫn không cần phải thoát cho chuỗi ở đây trong chuỗi thay thế?
Bryce Guinta

2
@BryceGuinta Bởi vì không giống như một cái gì đó giống như echo "\"hello\"", thứ bên trong $(...)không phải là một chuỗi được truyền dưới dạng đối số cho một chương trình khác và shell biết cách xử lý các dấu ngoặc kép. Đây cũng là lý do tại sao sử dụng $(...)chứ không phải backticks là tốt hơn; bạn có thể viết $( ... $( ... ) )mà không có bất kỳ sự mơ hồ nào, trong khi điều tương tự sử dụng backticks là ... khó xử.
Kusalananda

nhưng làm thế nào để làm trong bash không sử dụng bc
voldemort619

@ voldemort619 Bạn sẽ phải thực hiện additiion theo cách tương tự như bất kỳ một trong những thư viện này. Bạn có thể xem câu trả lời StackOverflow này để được giải thích. Nhưng thực sự, chỉ cần sử dụng bc.
Kusalananda

3

Như cả StéphaneKusalananda đều nói , "thực sự, chỉ sử dụng bc", nhưng nếu bạn thực sự muốn sử dụng bash để thêm vào, thì đây là điểm khởi đầu (chỉ số nguyên dương) - Tôi sẽ để nó như một bài tập để người đọc thực hiện số thập phân và số âm:

function arbadd {
  addend1=$1
  addend2=$2
  sum=
  bcsum=$(echo $addend1 + $addend2 | BC_LINE_LENGTH=0 bc)

  # zero-pad the smallest number
  while [ ${#addend1} -lt ${#addend2} ]
  do
    addend1=0${addend1}
  done

  while [ ${#addend2} -lt ${#addend1} ]
  do
    addend2=0${addend2}
  done

  carry=0
  for((index=${#addend1}-1;index >= 0; index--))
  do
    case ${carry}${addend1:index:1}${addend2:index:1} in
      (000) carry=0; sum=0${sum};;
      (001|010|100) carry=0; sum=1${sum};;
      (002|011|020|101|110) carry=0; sum=2${sum};;
      (003|012|021|030|102|111|120) carry=0; sum=3${sum};;
      (004|013|022|031|040|103|112|121|130) carry=0; sum=4${sum};;
      (005|014|023|032|041|050|104|113|122|131|140) carry=0; sum=5${sum};;
      (006|015|024|033|042|051|060|105|114|123|132|141|150) carry=0; sum=6${sum};;
      (007|016|025|034|043|052|061|070|106|115|124|133|142|151|160) carry=0; sum=7${sum};;
      (008|017|026|035|044|053|062|071|080|107|116|125|134|143|152|161|170) carry=0; sum=8${sum};;
      (009|018|027|036|045|054|063|072|081|090|108|117|126|135|144|153|162|171|180) carry=0; sum=9${sum};;
      (019|028|037|046|055|064|073|082|091|109|118|127|136|145|154|163|172|181|190) carry=1; sum=0${sum};;
      (029|038|047|056|065|074|083|092|119|128|137|146|155|164|173|182|191) carry=1; sum=1${sum};;
      (039|048|057|066|075|084|093|129|138|147|156|165|174|183|192) carry=1; sum=2${sum};;
      (049|058|067|076|085|094|139|148|157|166|175|184|193) carry=1; sum=3${sum};;
      (059|068|077|086|095|149|158|167|176|185|194) carry=1; sum=4${sum};;
      (069|078|087|096|159|168|177|186|195) carry=1; sum=5${sum};;
      (079|088|097|169|178|187|196) carry=1; sum=6${sum};;
      (089|098|179|188|197) carry=1; sum=7${sum};;
      (099|189|198) carry=1; sum=8${sum};;
      (199) carry=1; sum=9${sum};;
    esac
  done
  if [ $carry -eq 1 ]
  then
    sum=1${sum}
  fi
  printf "Sum = %s\n" "$sum"
}

Tôi đã để lại sự bcso sánh trong đó, nhưng nhận xét, để so sánh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.