C# 在不创建额外内存的情况下反转字符串

C# 在不创建额外内存的情况下反转字符串,c#,C#,我有一个大约1mb大小的字符串。要求在不分配另一个大小为1MB的临时字符串的情况下反转该字符串。我尝试了以下代码 string name = "abcde"; string target = ""; for(int i = name.Length - 1; i >=0; i--) { target += name[i]; name = name.Remove(i); int n = name.Length; } 但是我的朋友说,如

我有一个大约1mb大小的字符串。要求在不分配另一个大小为1MB的临时字符串的情况下反转该字符串。我尝试了以下代码

string name = "abcde";

string target = "";
for(int i = name.Length - 1; i >=0; i--)
{
    target += name[i];
    name = name.Remove(i);                
    int n = name.Length;
}

但是我的朋友说,如果我们使用函数
name.Remove(i)
,它将返回一个新字符串,但不能保证旧字符串会从内存中删除,因此不能保证大小会减小。这是真的吗?如果有其他选项可以在不分配额外内存的情况下反转一个字符串吗?

< P>编辑:完全失去了情节,最近使用C++太多了,我忘记了C字符串是不可变的。 简而言之,您必须分配新内存来更改C#中的字符串。您可以通过以下方式以最直接的方式完成此操作:

string ReverseString(string value)
{
    if (!string.IsNullOrEmpty(value))
    {
        char[] newBuffer = new char[value.Length];
        for(int i = 0; i < value.Length; i++)
            newBuffer[newBuffer.Length - i - 1] = value[i];
        value = new string(newBuffer);
    }
    return value;
}
    static IEnumerable<Tuple<int, int>> GetTextElementSegments(string value)
    {
        int[] elementOffsets = StringInfo.ParseCombiningCharacters(value);

        int lastOffset = -1;
        foreach (int offset in elementOffsets)
        {
            if (lastOffset != -1)
            {
                int elementLength = offset - lastOffset;
                Tuple<int, int> segment = new Tuple<int,int>(lastOffset, elementLength);
                yield return segment;
            }

            lastOffset = offset;
        }

        if (lastOffset != -1)
        {
            int lastSegmentLength = value.Length - lastOffset;

            Tuple<int, int> segment = new Tuple<int, int>(lastOffset, lastSegmentLength);
            yield return segment;
        }
    }

    static void Main(string[] args)
    {
        string input = "t\u0301e\u0302s\u0303t\u0304";
        StringBuilder resultBuilder = new StringBuilder(input.Length);

        var segments = GetTextElementSegments(input);

        foreach (var segment in segments.Reverse())
        {
            resultBuilder.Append(input, segment.Item1, segment.Item2);
        }

        Debug.Assert(resultBuilder.ToString() == "t\u0304s\u0303e\u0302t\u0301s");
    }
字符串反向限制(字符串值)
{
如果(!string.IsNullOrEmpty(值))
{
char[]newBuffer=new char[value.Length];
for(int i=0;i
这应该会有所帮助

string reverse = new string("ABCDEFGHI".ToCharArray().Reverse().ToArray());

字符串“abcde”在内存中是一个常量。你不能改变它,因为它是不变的。您需要的是创建一个新字符串,为此您需要新的内存。

字符串是不可变的,如果不分配新内存,您就无法反转它

string name = "aaaaa":
name = name.Remove(0); // this is allocating new memory.

字符串是不可变的——不能更改字符串对象的内容 创建对象后更改,尽管语法使其 看起来好像你能做到这一点

从同一链接,请参见此示例:

string b = "h";
b += "ello";
还有解释

编写此代码时,编译器实际上会创建一个新字符串 对象来保存新的字符序列,并且该新对象是 分配给b。然后字符串“h”有资格进行垃圾收集


字符串是不可变的。当你申报时,你不能更改它。因此,无论您尝试什么,都将创建并使用新的内存

string name = "aaaaa":
name = name.Remove(0); // this is allocating new memory.

如果您对原始数据有一些控制权,您应该能够做到这一点。例如,如果您可以要求使用
char[]
,而无需创建
字符串
,则可以在适当的位置将其反转

例如,在您的示例中,您可以使用
var name=new char[]{'a','b','c','d','e'}
代替字符串,然后将其颠倒过来

(显然,对于1MB字符串,您不能这样做,但无论您从何处获取字符串,如果您最初可以将其作为
char[]
加载…)


如果你只能有一个
字符串
,那你就倒霉了。它们是不可变的-您只能通过某种方式复制字符串来修改它。

使用
StringBuilder
您可以使用char数组进行手动操作,但不能使用
string
,因为它是不可变的

实际上,如果不至少分配与要反转的字符串占用的内存相同的内存量,就没有正确的方法来反转字符串。(从理论上讲,可以在内存中反转字符串,但这是不推荐的,我甚至不打算进入该区域)。现在我们来谈谈字符串本身的反转。我正在进行一个名为StringExtensions的小项目,在这个项目中,我试图解决使用字符串时可能出现的所有问题。最大的难题之一是UTF-16编码,该编码在整个框架中广泛使用。我的字符串反转实现如下所示:

string ReverseString(string value)
{
    if (!string.IsNullOrEmpty(value))
    {
        char[] newBuffer = new char[value.Length];
        for(int i = 0; i < value.Length; i++)
            newBuffer[newBuffer.Length - i - 1] = value[i];
        value = new string(newBuffer);
    }
    return value;
}
    static IEnumerable<Tuple<int, int>> GetTextElementSegments(string value)
    {
        int[] elementOffsets = StringInfo.ParseCombiningCharacters(value);

        int lastOffset = -1;
        foreach (int offset in elementOffsets)
        {
            if (lastOffset != -1)
            {
                int elementLength = offset - lastOffset;
                Tuple<int, int> segment = new Tuple<int,int>(lastOffset, elementLength);
                yield return segment;
            }

            lastOffset = offset;
        }

        if (lastOffset != -1)
        {
            int lastSegmentLength = value.Length - lastOffset;

            Tuple<int, int> segment = new Tuple<int, int>(lastOffset, lastSegmentLength);
            yield return segment;
        }
    }

    static void Main(string[] args)
    {
        string input = "t\u0301e\u0302s\u0303t\u0304";
        StringBuilder resultBuilder = new StringBuilder(input.Length);

        var segments = GetTextElementSegments(input);

        foreach (var segment in segments.Reverse())
        {
            resultBuilder.Append(input, segment.Item1, segment.Item2);
        }

        Debug.Assert(resultBuilder.ToString() == "t\u0304s\u0303e\u0302t\u0301s");
    }
静态IEnumerable GetTextElementSegments(字符串值)
{
int[]elementOffsets=StringInfo.ParseCombiningCharacters(值);
int lastOffset=-1;
foreach(元素偏移量中的整数偏移量)
{
如果(lastOffset!=-1)
{
int elementLength=offset-lastOffset;
元组段=新元组(lastOffset,elementLength);
收益率区间;
}
lastOffset=偏移量;
}
如果(lastOffset!=-1)
{
int lastSegmentLength=值。Length-lastOffset;
元组段=新元组(lastOffset,lastSegmentLength);
收益率区间;
}
}
静态void Main(字符串[]参数)
{
字符串输入=“t\u0301e\u0302s\u0303t\u0304”;
StringBuilder resultBuilder=新StringBuilder(input.Length);
var segments=GetTextElementSegments(输入);
foreach(段中的var段。Reverse())
{
resultBuilder.Append(输入,段.Item1,段.Item2);
}
Assert(resultBuilder.ToString()=“t\u0304s\u0303e\u0302t\u0301s”);
}

请注意,这会处理代理项对、unicode标记代码点,并且只分配与输入字符串本身占用的内存量相似的内存。

如果您真的不想额外分配内存,则需要
修复字符串并使用
不安全的
指针代码。或者,使用反射获取对内部
char[]
的引用

你真的不想这么做。真正地我是说,真的。请不要这样做,除非作为学习练习。如果人们在生产中发现这样的代码,他们可能会带着武器来找你

我不打算尝试编写这样的代码(因为我知道我会出错)。但这里有一些链接可以帮助您开始:

  • -一篇很老的文章,但有最好的例子
  • -对你想要的东西来说有点高,但这是一个好地方 开始
课程计划
{
静态void Main(字符串[]参数)
{
//注意我是如何不用变量的?
//这是一个肮脏的黑客知道编译器将实习的价值。
//破解BCL的诸多缺点之一。
while(string.IsNullOrWhiteSpace(Console.ReadLine()))
{
Console.WriteLine(“he\0llo”);
“he\0llo”。ReverseInPlace();
 for (int i = textBox1.TextLength - 1; i >= 0; i--)
 {
    textBox1.Text += textBox1.Text[i];
 }
 textBox1.Text = textBox1.Text.Remove(0, textBox1.Text.Length / 2);
public static String reverseString(String str) {
    return reverseStringXor(str.toCharArray(), 0, str.length() - 1);
}

private static String reverseStringXor(char[] str, int start, int end) {
    while (start < end) {
        str[start] ^= str[end];
        str[end] ^= str[start];
        str[start] ^= str[end];
        start++;
        end--;
    }
    return String.valueOf(str);
}