英文:
Acessing unexported function `nat.string`
问题
我想将一个big.Int转换为简单的base32格式。不是像base32实现的RFC 4648标准base32格式,也不是zBase32或Crockford,我只想要每个字符5位的普通0-9A-V字符集。
我知道有base32包,但它不能满足我的需求——它构建的结果是带有填充和其他我不想要的标准base32数字。当然,我可以使用它,然后去掉末尾的“=”字符,对剩下的部分进行修改,但这似乎是一个粗暴的解决方案。
有一个big.SetString(string, base)可以解析字符串形式的base32数字,但没有相反的操作——这才是我真正想要的,类似于Java的BigInteger.toString(int base)的big.GetString(base)。
然而,有一个nat.string正好满足我的需求。我如何获得对它的访问权限?
有没有办法我可以手动扩展big来实现big.GetString(base),它简单地调用具有正确charset的nat.string?
有没有办法我可以访问big使用的未导出的big.nat,并调用nat.string?
还有其他什么我可以做的吗?
附注:我也对使用nat.trailingZeroBits感兴趣——我不得不自己编写它,因为我没有意识到这已经存在。
英文:
I want to convert a big.Int to simple base32. Not the standard base32 stuff like the RFC 4648 implemented by base32 nor zBase32 nor Crockford I want just simple normal 5-bits per character 0-9A-V character set.
I am aware of the base32 package and it does not do what I want - it builds the result in a standard base 32 number with padding and stuff I don't want. Certainly I could use it and tear off the trailing "=" characters and hack what remains but that just seems like a brutal solution.
There is a big.SetString(string, base) that can parse a base32 number in string form but there is no reverse - which is what I am really looking for, a big.GetString(base) like the Java BigInteger.toString(int base).
There is, however, a nat.string which does exactly what I want. How can I gain access to it?
Is there a way I could manually extend big to implement big.GetString(base) which trivially calls nat.string with the correct charset?
Is there a way I can reach into the unexported big.nat which big uses and call nat.string?
Is there something else I can do?
P.S. I'd also be interested in using nat.trailingZeroBits - I had to write my own because I didn't realise this was already done.
答案1
得分: 2
你无法访问未导出的函数。为了实现该功能,你需要重新编写nat.go的至少一部分代码。其中一些函数看起来非常有用,所以值得考虑向golang-nuts组发送一个功能请求,要求在未来的版本中将其中一些函数导出。
但是,你可以使用strconv.FormatInt()来实现你的需求。
给定一个big.Int类型的变量b,你可以这样做:
strconv.FormatInt(b.Int64(), 32)
完整示例:
package main
import (
	"fmt"
	"math/big"
	"strconv"
)
func main() {
	i := 3286583923486565782 // 一个随机整数
	b := big.NewInt(int64(i))
	fmt.Println(strconv.FormatInt(b.Int64(), 32))
}
输出结果:
>>2r72al99uq9cm
英文:
You can't access unexported functions at all. You'd need to re-write at least some portion of nat.go in order to achieve that functionality. Some of those functions look very useful, so it may be worth sending a feature request to the golang-nuts group asking for some of them to be exported in a future release.
You can however use strconv.FormatInt() to do what you require.
given a big.Int b you can do:
strconv.FormatInt(b.Int64(), 32)
Full example:
package main
import (
	"fmt"
	"math/big"
	"strconv"
)
func main() {
	i := 3286583923486565782 // Some random integer
	b := big.NewInt(int64(i))
	fmt.Println(strconv.FormatInt(b.Int64(), 32))
}
Produces:
>>2r72al99uq9cm
答案2
得分: 2
我不认为Go团队会在nat中导出任何东西,因为那是big.Int的实现细节。不过,他们可能会对从big.Int导出一个ToBase函数的请求持开放态度。
与此同时,这是一个经过轻度测试的简单进制转换器的代码:
func ToBase(x *big.Int, base int) string {
    if x.Sign() == 0 {
        return "0"
    }
    y := new(big.Int).Set(x)
    b := big.NewInt(int64(base))
    charset := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    out := make([]byte, 0, 16)
    negative := false
    if y.Sign() < 0 {
        negative = true
        y.Neg(y)
    }
    digit := new(big.Int)
    for y.Sign() != 0 {
        y.DivMod(y, b, digit)
        out = append(out, charset[digit.Int64()])
    }
    if negative {
        out = append(out, '-')
    }
    // Reverse out
    for i, j := 0, len(out)-1; i < j; i, j = i+1, j-1 {
        out[i], out[j] = out[j], out[i]
    }
    return string(out)
}
英文:
I don't think the go team will ever export anything in nat as that is an implementation details for big.Int.  They may look kindly upon a request to export a ToBase function though from big.Int.
In the mean time here is a lightly tested naive base converter for you
func ToBase(x *big.Int, base int) string {
	if x.Sign() == 0 {
		return "0"
	}
	y := new(big.Int).Set(x)
	b := big.NewInt(int64(base))
	charset := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	out := make([]byte, 0, 16)
	negative := false
	if y.Sign() < 0 {
		negative = true
		y.Neg(y)
	}
	digit := new(big.Int)
	for y.Sign() != 0 {
		y.DivMod(y, b, digit)
		out = append(out, charset[digit.Int64()])
	}
	if negative {
		out = append(out, '-')
	}
	// Reverse out
	for i, j := 0, len(out)-1; i < j; i, j = i+1, j-1 {
		out[i], out[j] = out[j], out[i]
	}
	return string(out)
}
答案3
得分: 1
我已经在go-nuts线程上发布了。他们对我的建议似乎很满意。
同时 - 我的Base32实现(不处理符号 - 类似于nat)
// 数字
const charset string = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
/*
将大数转换为Base32
*/
func Base32(n big.Int) string {
// 数字的字节
bytes := n.Bytes()
nBytes := len(bytes)
if nBytes == 0 {
// 特殊情况0。
return "0"
}
// 字符串中将有多少个数字?
nBits := nBytes * 8
nDigits := nBits / 5
if nBits%5 > 0 {
nDigits += 1
}
// 将数字扩展为数组
digits := make([]byte, nDigits)
digit := nDigits - 1
// 每次从数组中取出5位。
for b, shr := nBytes-1, uint(0); b >= 0; {
// 下一批在那里。
shrp5 := shr + 5
// 构建我的位。
x := (bytes[b] >> shr)
if shrp5 > 8 && b > 0 {
of := shrp5 - 8
// 从下一个字节中添加一些位。
x &= (1 << (8 - shr)) - 1
x |= bytes[b-1] << (5 - of)
}
// 再移动5位。
shr = shrp5
// 我们是否应该跳到下一个字节?
if shr >= 8 {
// 下一个字节
shr -= 8
b -= 1
}
x &= 0x1f
// 构建我的数字
digits[digit] = charset[x]
digit -= 1
}
// 跳过前导零。
lz := 0
for digits[lz] == '0' {
lz += 1
}
// 返回等效的字符串。
return string(digits[lz:])
}
欢迎评论。
英文:
I have posted on the go-nuts thread. They seem happy with my suggestion.
Meanwhile - my implementation of Base32 (does NOT handle sign - like nat)
// The digits
const charset string = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
/*
 Convert the big to base32
*/
func Base32(n big.Int) string {
	// The bytes of the number
	bytes := n.Bytes()
	nBytes := len(bytes)
	if nBytes == 0 {
		// Special case 0.
		return "0"
	}
	// How many digits will be in the string?
	nBits := nBytes * 8
	nDigits := nBits / 5
	if nBits%5 > 0 {
		nDigits += 1
	}
	// Grow the digits into an array
	digits := make([]byte, nDigits)
	digit := nDigits - 1
	// Peel off 5 bits from the array each time.
	for b, shr := nBytes-1, uint(0); b >= 0; {
		// The next lot is there.
		shrp5 := shr + 5
		// Build my bits.
		x := (bytes[b] >> shr)
		if shrp5 > 8 && b > 0 {
			of := shrp5 - 8
			// Add in some bits from the next byte too.
			x &= (1 << (8 - shr)) - 1
			x |= bytes[b-1] << (5 - of)
		}
		// Shift 5 more.
		shr = shrp5
		// Should we step to next byte?
		if shr >= 8 {
			// Next byte
			shr -= 8
			b -= 1
		}
		x &= 0x1f
		// Build my digit
		digits[digit] = charset[x]
		digit -= 1
	}
	// Skip leading zeros.
	lz := 0
	for digits[lz] == '0' {
		lz += 1
	}
	// Return the string equivalent.
	return string(digits[lz:])
}
Comments welcome.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论