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

时间:2012-07-05 08:52:45

标签: c#

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

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)它将返回一个新的字符串,但不保证旧的字符串将从内存中删除,因此无法保证大小将减少。这是真的吗?如果有的话还有其他选项可以在不分配额外内存的情况下反转字符串吗?

14 个答案:

答案 0 :(得分:13)

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

答案 1 :(得分:5)

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

答案 2 :(得分:3)

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

String from MSDN

  

字符串是不可变的 - 字符串对象的内容不能   在创建对象后更改,尽管语法可以实现   好像你可以这样做。

从同一个链接看到这个例子:

string b = "h";
b += "ello";

和解释。

  

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

答案 3 :(得分:2)

字符串是不可变的。当您申报时,您无法更改它。所以无论你尝试什么,都会创造并使用新的记忆。

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

答案 4 :(得分:2)

class Program
{
    static void Main(string[] args)
    {
        //Note how I don't bother to use a variable?
        //This is a nasty hack that knows the compiler will intern the value.
        //One of the many downsides of hacking the BCL.
        while (string.IsNullOrWhiteSpace(Console.ReadLine()))
        {
            Console.WriteLine("he\0llo");  
            "he\0llo".ReverseInPlace();
        }
    }
}

public static class Helper
{
    //Does not support multi-char values.
    public static unsafe void ReverseInPlace(this string str)
    {
        fixed (char* pfixed = str)
            for (int i = 0, ii = str.Length - 1; i < str.Length / 2; i++, ii--)
            {
                var p1 = (char*)pfixed + i;
                var p2 = (char*)pfixed + ii;
                var temp = *p1;
                *p1 = *p2;
                *p2 = temp;
            }
    }
}

如果你使用它,你可能会死......或者其他什么。哎呀,我甚至不知道如何编写不安全的代码。

答案 5 :(得分:1)

如果您对原始数据有一定的控制权,那么您应该可以这样做。例如。如果您可以在没有创建char[]的情况下请求string,则可以将其撤消。

例如,在您的示例中,您可以使用var name = new char[] { 'a', 'b', 'c', 'd', 'e'}而不是字符串,然后将其反转。

(显然你不能为1MB的字符串做这个,但是无论你从哪里得到字符串,如果你最初可以把它作为char[]加载......)

如果你只能拥有string,那你就不走运了。它们是不可变的 - 你只能通过某种方式复制它来修改字符串。

答案 6 :(得分:1)

StringBuilder是Mutable,在这种情况下应该为您提供最佳效率

string inpuStr = "The quick brown fox jumped over the lazy dog...blah blah blah up to 1MB";
StringBuilder builder = new StringBuilder();
            for (int i = inpuStr.Length - 1; i >= 0; i--)
            {
                builder.Append(inpuStr[i]); 
            }
           return builder.ToString();

答案 7 :(得分:0)

编辑:完全丢失了剧情,最近我一直在使用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;
}

答案 8 :(得分:0)

you can try with linq to reverse

    string original = "abcde";
    string reverseValue = new string(original.Reverse().ToArray());

you can try also with 

string result= new string(original.Select((c, index) => new { c, index })
                                         .OrderByDescending(x => x.index)
                                         .ToArray());

答案 9 :(得分:0)

实际上没有正确的方法来反转字符串而不至少分配与要反转的字符串占用的相同内存量。 (理论上它可以在内存中反转一个字符串,但这是不推荐的,我甚至不会进入该区域)。现在至于反转字符串本身。我正在开发一个名为StringExtensions的小项目,其中我试图敲击使用字符串时可能发生的所有问题。其中一个最大的问题是使用UTF-16编码,该编码在整个框架中得到广泛使用。我对逆转字符串的实现看起来像这样:

    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");
    }

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

答案 10 :(得分:0)

如果您真的认真对待没有其他分配,则需要fix字符串并使用unsafe指针代码。或者,或许,使用反射来获取对内部char[]的引用。

你真的不想这样做。真。我的意思是,真的。请不要这样做,除非是学习练习。如果人们在生产中找到这样的代码,他们很可能会用武器来找你。

我不会尝试编写这样的代码(因为我知道我会弄错)。但是这里有一些链接可以让你开始:

答案 11 :(得分:0)

我刚刚通过使用字符串的 toCharArray() 函数和 XOR 操作来交换字符,为此做了一个 Java 解决方案:

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);
}

答案 12 :(得分:-1)

这应该有帮助

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

答案 13 :(得分:-1)

这是你的答案之一。

 for (int i = textBox1.TextLength - 1; i >= 0; i--)
 {
    textBox1.Text += textBox1.Text[i];
 }
 textBox1.Text = textBox1.Text.Remove(0, textBox1.Text.Length / 2);