5

私はこの男と同じ問題を抱えており、おそらくこの男と同じ問題を抱えていますが、コードを共有して質問に答えるために周りにいます!

pyodbc を介して Microsoft Access データベースからフィールドを読み取り、表示用に出力を準備するバッチ ジョブのコードがいくつかあります。

ここにスニペットがあります。アサートに注意してください。

def format_currency(amount):
    if amount is None:
        return ""
    else:
        result = "$%.2f" % amount
        assert ":" not in result, (
            "That's weird. The value %r of class %s is represented as %s" %
             (amount, amount.__class__, result))
        return result

実行すると、100,000 行を正常に処理してから失敗します。

AssertionError: That's weird. The value Decimal('54871.0000') of class <class
'decimal.Decimal'> is represented as $54870.:0

異常な結腸に注意してください。めったに発生しません - 300,000 レコードに約 1 回です。

もちろん、それを分離しようとすると、うまくいきます。

from decimal import Decimal
print "$%.2f" % Decimal('54871.0000')

$54871.00

Access のフィールドの型は次のとおりです。

  • データ型: 通貨
  • 小数点以下の桁数: 2
  • 入力マスク:
  • デフォルト値:
  • 検証規則:
  • テキストの配置: 一般

不十分な証拠に基づく私の漠然とした指差しの疑い: pyodbc は、おそらく Access の破損によって混乱している、Decimal の内部を突っついています。@ecatmur が指摘するように:

':' は ASCII の '9' + 1 です

これを見て解決した人いますか?

バージョン:

  • パイソン 2.7.4
  • pyodbc 3.0.6 (最新)
  • アクセス 2010
  • ウィンドウズ7

さらに掘り下げる:

decimalモジュールは Python で実装されています。_exp私の読書によると、値は、_int_sign、の 4 つの属性で表されます。_is_special

破損を疑って、これらのフィールドの値を出力しました。

驚いたことに、障害のあるバージョンと動作中のバージョンの両方で、次のようになります。

_exp: -4
_int: 548710000
_sign: 0
_is_special: False

それは変だ。


decimalモジュールでは、__float__関数はかなり単純に定義されています。

def __float__(self):
    """Float representation."""
    return float(str(self))

しかし、悪いデータでこれを行うと:

print "Str", str(amount)
print "Float", float(amount)

私は得る:

筋力 54871.0000

フロート 54870.:

学べば学ぶほど、奇妙に感じなくなります。

4

1 に答える 1

1

エラーを再現できました。Access テーブル [pyData] を作成しました...

ID - 自動付
番金額 - 通貨 (小数点第 2 位)

...そして、50,000 から 60,000 の間の 100 万行のランダムな値で埋めました。テストスクリプトを実行すると、ここで失敗しました

30815 : $50638.91
30816 : $52423.28
30817 :

Traceback (most recent call last):
  File "C:\__tmp\pyOdbcTest.py", line 20, in <module>
    print row.ID, ":", format_currency(row.Amount)
  File "C:\__tmp\pyOdbcTest.py", line 10, in format_currency
    (amount, amount.__class__, result))
AssertionError: That's weird. The value Decimal('58510.0000') of class <class 'decimal.Decimal'> is represented as $5850:.00

また、その値 (58510.00) と失敗した値 (54871.00) を、同じ構造を持つ別のテーブルの単一行としてテストしましたが、両方とも失敗しました。したがって、これは以前の ODBC 呼び出しから残った「がらくた」の機能ではないことがわかります。

数字の最後に「1」の後にゼロが続く数字に関連しているのではないかと考えて、55871.00 を試してみましたが、うまくいきました。53871.00 も問題なく動作しました。番号を 54871.00 に戻すと、エラーが復活しました。

pypyodbcを使用して同じテストを試みたところ、同じエラーが発生しました。pypyodbc には多くの Access 固有の機能が含まれているため、私はやや楽観的でした。そのため、pypyodbc のユーザーの 1 人が以前にこの問題に遭遇した可能性があると考えましたが、明らかにそうではありませんでした。

最後に、テスト テーブルを SQL Server 2008 R2 Express にアップサイズし、{SQL Server Native Client 10.0} ドライバーを使用して同じテストを試みました。Access ("Currency" 列の種類) から読み取ったときに失敗した数値は、SQL Server テーブル ("money" 列の種類) から読み取ったときに失敗しませんでした。

したがって、現時点で「答え」として提供できる最善の方法は次のとおりです。

次のいずれかのようです。

  • pyodbc (および pyodbc と非常に密接に関連しているように見える pypyodbc) のバグ、または

  • Microsoft Access ODBC ドライバーのバグ、または

  • 2 つの間の「不幸な相互作用」 (ODBC 仕様が十分に緩く、どちらのコンポーネントも技術的に「間違っている」とは言えない場合)。

いずれにせよ、少なくとも今のところは、回避する必要があるようです。

編集

膨大な数のバッチがあったので、スクリプトを実行し続けて、コロンでフォーマットされた他の数値を確認することにしました。結果のリストはすべて整数 (ペニーではありません) のように見えたので、1 から 100,000 までの整数で別のテストを実行しました。フォーマットされた文字列にコロンが含まれる 260 個の数字を見つけました。

1451.0000 -> $1450.:0
1701.0000 -> $1700.:0
1821.0000 -> $1820.:0
1951.0000 -> $1950.:0
2091.0000 -> $2090.:0
...
98621.0000 -> $98620.:0
98710.0000 -> $9870:.00
99871.0000 -> $99870.:0

リスト全体をここに貼り付けました。もしかしたら、それが役に立つかもしれません。

編集 - 問題解決 (?)

以前のテストは、Python バージョン 2.7.3 で実行されました。Python をバージョン 2.7.5 (Win 32 ビット) にアップグレードしたところ、pyodbc はバージョン 3.0.6 のままで、問題は解決したようです。

于 2013-05-18T13:09:55.273 に答える